/* ==========================================
BASE GAME VARIABLES
========================================== */
// Game Information
<<set setup.gameName = "All That Glitters">>
<<set setup.gameVersion = "Beta">>
<<set $saveVersion to (setup.CURRENT_SAVE_VERSION || 0)>>
/* ==========================================
UI VISIBILITY SETTINGS
========================================== */
// Topbar
<<set $hideTopbar to false>>
<<set $hideTopbarHamburger to false>>
<<set $hideTopbarNavLeft to false>>
<<set $hideTopbarNavRight to false>>
<<set $hideTopbarTimebox to false>>
<<set $hideTopbarNotifications to false>>
// Rightbar
<<set $hideRightbar to false>>
<<set $hideRightbarGameInfo to false>>
<<set $hideRightbarProfile to false>>
<<set $hideRightbarStats to false>>
<<set $hideRightbarPhone to false>>
<<set $hideRightbarMap to true>>
/* ==========================================
TOPBAR NOTIFICATIONS
========================================== */
<<set $notificationEnergy = 0>>
<<set $notificationHealth = 0>>
<<set $notificationMood = 0>>
<<set $notificationArousal = 0>>
<<set $notificationBladder = 0>>
<<set $notificationThirst = 0>>
<<set $notificationHunger = 0>>
<<set $notificationHygiene = 0>>
<<set $notificationCum = 0>>
<<set $notificationHeart = 0>>
<<set $notificationBed = 1>>
<<set $notificationAlarm = 1>>
<<set $notificationSchool = 0>>
<<set $notificationClothes = 0>>
<<set $notificationFace = 0>>
<<set $notificationFaceText = "">>
/* ==========================================
PHONE – Data model (phone_system_data_and_technical.md)
Badge counts are derived from state; no manual counters.
========================================== */
<<set $notificationPush = 0>>
<<set $phoneConversations = {}>>
<<set $phoneFotogramPosts = []>>
<<set $phoneFotogramDMs = []>>
<<set $phoneFollowers = 0>>
<<set $phoneLastFotogramPostDate = null>>
<<set $phoneFotogramCooldownDays = 0>>
<<set $phoneNotifications = { fotogram: [], finder: [] }>>
<<set $phoneFinderProfile = { galleryIds: [] }>>
<<set $phoneFinderMatches = []>>
<<set $phoneCallsLog = []>>
<<set $phoneAppointments = []>>
<<set $phoneFameEventsFired = []>>
<<set $phoneContactPromoted = {}>>
<<set $phoneContactsUnlocked = []>>
<<set $phoneBlocked = []>>
<<set $phoneGeneratedContacts = {}>>
<<set $phoneFotogramRandomSwapIds = []>>
<<set $phonePromotedContactCounter = 0>>
<<set $phoneWhereAskedLast = {}>>
<<set $phoneTalkAskedLast = {}>>
<<set setup.phoneMessageTopics = {}>>
<<set setup.phoneCameraDailyEnabled = true>>
<<set setup.phoneCameraDailyPhotoLimit = 1>>
<<set setup.phoneCameraDailyVideoLimit = 1>>
<<set setup.phoneCameraDailyBypass = false>>
<<set $phoneCameraDailyBypass = false>>
<<set $phoneCameraDaily = { dateKey: 0, photosTaken: 0, videosTaken: 0 }>>
/* ==========================================
DAILY TRACKING
========================================== */
<<set $daily = {
yogaDone: false,
danceDone: false,
jogDone: false,
snoozeCount: 0,
hairCreamUsed: false,
faceCreamUsed: false,
dentalCareUsed: false,
magazineStatUsedToday: false,
bookMinutesRead: 0,
dishwashRushSurgeToday: false,
dishwashVinceCheckToday: false,
/* Last game day we rolled for late-night parents' room sounds (fhDownstairs, hour 0); one roll per night */
fhThinWallsNight: 0
}>>
/* Arousal ambient cooldown trackers */
<<set $afterShowerMirrorLastDayKey = 0>>
<<set $brotherPornSecondLastSeenDayKey = 0>>
<<set $brotherPornPhase2Step = 0>>
<<set $streetAttentionLastMinuteStamp = 0>>
<<set $streetAttentionLastVariant = "">>
<<set $streetAttentionVariantLastMinute = {}>>
/* Night restless / try-sleep baseline (v0.1.5 prep) */
<<set $nightRestlessEventDayKey = 0>>
<<set $nightRestlessEventSeenCount = 0>>
<<set $nightRestlessSource = "">>
<<set $porn = {
categoriesUnlocked: ["handjob"],
categoryWatchCounts: { handjob: 0, blowjob: 0, vanilla: 0, anal: 0, lesbian: 0, bbc: 0, creampie: 0, mff: 0, mmf: 0, gangbang: 0, incest: 0 },
brotherRoomCategoryWatchCounts: { handjob: 0, blowjob: 0, vanilla: 0, anal: 0, lesbian: 0, bbc: 0, creampie: 0, mff: 0, mmf: 0, gangbang: 0, incest: 0 },
brotherRoomCategoryDailyWatchCounts: { handjob: 0, blowjob: 0, vanilla: 0, anal: 0, lesbian: 0, bbc: 0, creampie: 0, mff: 0, mmf: 0, gangbang: 0, incest: 0 },
brotherRoomCategoryDailyDayKey: 0,
brotherRoomCategoriesCountedToday: [],
categoryProgressCounts: { handjob: 0, blowjob: 0, vanilla: 0, anal: 0, lesbian: 0, bbc: 0, creampie: 0, mff: 0, mmf: 0, gangbang: 0, incest: 0 },
categoryProgressDayKey: 0,
categoriesProgressedToday: [],
categoryTargets: { handjob: random(3, 5) },
pendingUnlock: "",
selectedCategory: "",
returnPassage: "brotherComputerMenu",
source: "brotherRoom",
arousalDayKey: 0,
watchCountToday: 0,
maxDailyWatches: 6,
skillGainDayKey: 0,
skillsGainedToday: [],
categoryFirstWatchSeen: { handjob: false, blowjob: false, vanilla: false, anal: false, lesbian: false, bbc: false, creampie: false, mff: false, mmf: false, gangbang: false, incest: false },
handjobFirstScenePlayed: false,
blowjobFirstScenePlayed: false,
repeatVidSrc: "",
repeatVidWidth: "100%"
}>>
/* Hospital daily cooldown trackers */
<<set $hospitalLastDoctorDayKey = 0>>
/* Makeup (state/style/quality + hourly decay tracking; makeup score computed in recalculateStats) */
<<set $makeup = { state: "off", style: 0, quality: 0, lastDecayHourKey: "", lastAppliedHourKey: "", lastDecayMinuteStamp: 0 }>>
/* Mirror routine: saved on Confirm; Apply Routine button shows only when confirmed */
<<set $mirrorRoutine = { makeup: 0, combHair: true, hairCream: true, faceCream: true, dentalCare: true }>>
<<set $mirrorRoutineConfirmed = false>>
/* Hospital system state (v1). Debt escalation events can hook into dueDateKey later. */
<<set $hospitalDebt = 0>>
<<set $hospitalDebtSinceDateKey = 0>>
<<set $hospitalDebtDueDateKey = 0>>
<<set $hospitalLastAdmissionDateKey = 0>>
<<set $hospitalAdmissionInProgress = false>>
<<set $hospitalAdmissionCount = 0>>
/* ==========================================
SYSTEM VARIABLES
========================================== */
/* Alarm System */
<<set $alarm = {
weekdayEnabled: false,
weekdayHour: 7,
weekdayMinute: 0,
weekendEnabled: false,
weekendHour: 10,
weekendMinute: 0
}>>
/* Flags System - For story events/states */
<<set $flags = {
fatherStartedWork: false,
fatherShowerPeekFirstSeen: false,
motherShowerPeekFirstSeen: false,
brotherShowerPeekFirstSeen: false,
dianaEventShown: false,
dianaGossipUnlocked: false,
dianaThoughtsShown: false,
sofiaGossipUnlocked: false,
emmaGossipDone: false,
sofiaGossipDone: false,
vinceInspectionDay3Shown: false,
vinceDay3InspectShift: 0,
vinceDay3FamilyOpen: false,
vinceDay3FamilyMomDone: false,
vinceDay3FamilyBrotherDone: false,
vinceDay3FamilyFatherDone: false,
vinceDay3FamilyBedroomDone: false,
jakeCoffeeInviteWeek3Shown: false,
firstWorkDayWalkEvent: false,
firstWorkDayWalkHome: false,
gymMembershipActive: false,
bodyAwarenessTier1: false,
bodyAwarenessTier2: false,
bodyAwarenessTier3: false,
brotherPornFirstSeen: false,
brotherPornSecondPeek: false,
brotherPornLookButtonUnlocked: false,
brotherPornWatchFirstDone: false,
pornWatchFirstDone: false,
privateContentHabit: false,
firstCantSleepStoryDone: false,
masturbationUnlocked: false,
nightRestlessUnlocked: false
}>>
/* Gym membership: 30-day access, decremented by advanceDay. */
<<set $gymMembershipDaysRemaining = 0>>
<<set $gymMembershipStartedAt = null>>
/* Important Dates */
<<set $importantDates = {
fatherWorkStart: { year: 2025, month: 8, day: 25 }
}>>
/* ==========================================
JOB SYSTEM STATE (Global – single job)
Details: 1Developer/JobSystemDesign.md
========================================= */
<<set $job = {
id: null,
tier: 1,
startedOn: null
}>>
<<set $jobState = {
hoursThisWeek: 0,
hoursToday: 0,
shiftsToday: 0,
totalDaysWorked: 0,
jobExperience: 0,
weeklyEarnings: 0,
weeklyDeductions: 0,
lastPayWeek: null,
lastShiftEndHour: null,
minHoursWarningCount: 0,
firstWorkDayEventShown: false,
insufficientDaysThisWeek: 0,
bossWantsToSeePlayer: false,
visitedBossThisMonday: false,
freeMealUsedToday: false,
workedToday: false,
workTrustScore: 60,
workTrustBand: "mid",
bossMeetingRequired: false,
bossMeetingReason: "",
terminationPending: false,
promotionPending: false,
pendingTier: 0,
weeklyPayPending: null,
lastInsufficientType: "",
lastInsufficientHours: 0,
excuseForDateKey: 0,
excuseMode: "",
excuseSentToday: false,
bossLieAttemptedThisMeeting: false,
bossLieLastResult: "",
bossLieLastReason: "",
bossLieLastKey: "",
bossLieLastChance: 0,
bossLieLastPenaltyAmount: 0
}>>
/* School calendar - cyclical, repeats every year */
/* Uses month/day only, works for any year */
<<set setup.schoolCalendar = {
/* Vacation periods - brother uses "vacation" schedule during these */
vacations: [
{ name: "summerVacation", startMonth: 6, startDay: 12, endMonth: 9, endDay: 7 }, /* Jun 12 - Sep 7 */
{ name: "semesterBreak", startMonth: 1, startDay: 23, endMonth: 2, endDay: 7 } /* Jan 23 - Feb 7 */
]
}>>
<<set setup.charGeneratorMaleFirstNames = ["Luca", "Noah", "Ethan", "Leo", "Mason", "Liam", "Ezra", "Caleb", "Owen", "Finn", "Jake", "Ryan", "Cole", "Blake", "Dylan", "Jordan", "Kai", "Max", "Reed", "Shane"]>>
<<set setup.charGeneratorFemaleFirstNames = ["Aria", "Mila", "Nora", "Sofia", "Lena", "Ivy", "Elena", "Maya", "Zoe", "Chloe", "Emma", "Olivia", "Ava", "Luna", "Stella", "Violet", "Hazel", "Ruby", "Jade", "Iris"]>>
<<set setup.charGeneratorUnisexFirstNames = ["Alex", "Sam", "Riley", "Taylor", "Jordan", "Morgan", "Casey", "Quinn", "Avery", "Skyler", "Jamie", "Cameron", "Dakota", "Parker", "Reese", "Sage", "River", "Phoenix", "Drew", "Blair"]>>
<<set setup.charGeneratorLastNames = ["Carter", "Hayes", "Brooks", "Reed", "Bennett", "Morris", "Parker", "Cole", "Morgan", "Hayden", "Sullivan", "Gray", "Foster", "West", "Stone", "Cross", "Fox", "Hart", "Lane", "Shaw"]>>
<<run setup.charGeneratorEthnicities = [
{ id: "white_western", label: "White/Western", weight: 30, preferredSkinTones: ["white", "tan"] },
{ id: "black_african_diaspora", label: "Black/African Diaspora", weight: 20, preferredSkinTones: ["black"] },
{ id: "latino", label: "Latino", weight: 18, preferredSkinTones: ["tan", "white"] },
{ id: "middle_eastern", label: "Middle Eastern", weight: 12, preferredSkinTones: ["tan", "white"] },
{ id: "south_asian", label: "South Asian", weight: 10, preferredSkinTones: ["tan", "black"] },
{ id: "east_asian", label: "East Asian", weight: 10, preferredSkinTones: ["white", "tan"] }
]>>
<<run setup.charGenerator = {
maleWeight: 0.52,
femaleWeight: 0.48,
maleFirstNames: setup.charGeneratorMaleFirstNames,
femaleFirstNames: setup.charGeneratorFemaleFirstNames,
unisexFirstNames: setup.charGeneratorUnisexFirstNames,
lastNames: setup.charGeneratorLastNames,
ageMin: 18,
ageMax: 60,
skinTones: ["white", "black", "tan"],
occupations: ["Student", "Barista", "Retail Worker", "Freelancer", "Office Assistant", "Gym Staff", "Receptionist"],
statuses: ["Online Contact", "DM Contact", "New Contact"],
colors: ["#a855f7", "#3b82f6", "#f97316", "#14b8a6", "#ec4899"],
relations: {
locationId: "maplewood",
placeId: "onlineContacts",
placeName: "Online Contacts"
},
ethnicities: setup.charGeneratorEthnicities,
heightRanges: {
male: { min: 168, max: 198 },
female: { min: 150, max: 182 }
},
maleBody: {
penisMinCm: 10,
penisMaxCm: 22
},
femaleBody: {
bustMinCm: 78, bustMaxCm: 108,
waistMinCm: 56, waistMaxCm: 84,
hipsMinCm: 82, hipsMaxCm: 120,
hairMinCm: 8, hairMaxCm: 90
},
avatarPool: {
male: { white: [], black: [], tan: [] },
female: { white: [], black: [], tan: [] }
},
initialState: {
stats: { love: 0, friendship: 25, lust: 0, trust: 5 },
opinion: { awareness: 0, flags: [] },
firstMet: null,
known: true,
currentLocation: null,
currentStatus: null
},
meetupFallbackSlots: [
{ hour: 13, minute: 0 },
{ hour: 16, minute: 0 },
{ hour: 18, minute: 0 },
{ hour: 21, minute: 0 }
]
}>>
<<run
(function() {
var base = "assets/content/people/charGenerator/";
for (var i = 1; i <= 20; i++) {
setup.charGenerator.avatarPool.female.white.push(base + "female/w" + i + ".webp");
setup.charGenerator.avatarPool.male.white.push(base + "male/mw" + i + ".webp");
setup.charGenerator.avatarPool.male.black.push(base + "male/mb" + i + ".webp");
}
})();
>>/* ==========================================
LOCATION DISCOVERY FLAGS
Controls which locations are visible in navigation
Set to false to hide, true or undefined to show
========================================== */
<<nobr>>
/* Default: Only FamilyHouse is discovered at start */
/* ==========================================
MAPLEWOOD - Start Area (Discovered)
========================================== */
<<set $discoveredMaplewood = true>>
<<set $discoveredFamilyHouse = true>>
<<set $discoveredFhDownstairs = true>>
<<set $discoveredFhUpperstairs = true>>
<<set $discoveredFhBedroom = true>>
<<set $discoveredFhWardrobe = true>>
<<set $discoveredFhKitchen = true>>
<<set $discoveredFhLivingroom = true>>
<<set $discoveredFhBrotherRoom = true>>
<<set $discoveredFhUpperBath = true>>
<<set $discoveredFhDownbath = true>>
<<set $discoveredFhParentsRoom = true>>
<<set $discoveredFhParentsBath = true>>
<<set $discoveredFhBackyard = true>>
/* Maplewood neighborhood - needs discovery */
<<set $discoveredMapleChurch = false>>
<<set $discoveredStoreCorner = false>>
<<set $discoveredSunsetPark = false>>
/* ==========================================
DOWNTOWN - Needs Discovery
========================================== */
<<set $discoveredDownTown = false>>
<<set $discoveredSkyline = false>>
<<set $discoveredBank = false>>
<<set $discoveredCityHall = false>>
<<set $discoveredHospital = false>>
<<set $discoveredHotel = false>>
<<set $discoveredNightClub = false>>
<<set $discoveredRestaurantDowntown = false>>
/* Mall */
<<set $discoveredMall = false>>
<<set $discoveredFloorGround = false>>
<<set $discoveredFloorSecond = false>>
<<set $discoveredFloorThird = false>>
<<set $discoveredStoreClothingA = false>>
<<set $discoveredStoreCosmetics = false>>
<<set $discoveredStoreElectronics = false>>
<<set $discoveredStoreShoeA = false>>
<<set $discoveredStoreSports = false>>
<<set $discoveredStoreBags = false>>
<<set $discoveredStoreClothingB = false>>
<<set $discoveredStoreClothingC = false>>
<<set $discoveredStoreJewelry = false>>
<<set $discoveredStoreLingerieA = false>>
<<set $discoveredStoreLingerieB = false>>
<<set $discoveredStoreShoeB = false>>
<<set $discoveredFoodCourtMall = false>>
<<set $discoveredSalonBeautyMall = false>>
<<set $discoveredTheatreMovie = false>>
/* Tower A */
<<set $discoveredTowerA = false>>
<<set $discoveredRecreationCenter = false>>
<<set $discoveredCourtBasketball = false>>
<<set $discoveredCourtVolleyball = false>>
<<set $discoveredGym = false>>
<<set $discoveredGymMaleLocker = false>>
<<set $discoveredGymMaleLockerShower = false>>
<<set $discoveredGymFemaleLocker = false>>
<<set $discoveredGymFemaleLockerWardrobe = false>>
/* Tower B & C */
<<set $discoveredTowerB = false>>
<<set $discoveredTowerC = false>>
<<set $discoveredJazzClub = false>>
<<set $discoveredPethouseSuite = false>>
<<set $discoveredRestaurantRoof = false>>
<<set $discoveredRoofTopLounge = false>>
/* ==========================================
HILLCREST - Needs Discovery
========================================== */
<<set $discoveredHillcrest = false>>
<<set $discoveredGalleryArt = false>>
<<set $discoveredGolfClub = false>>
<<set $discoveredGolfCourse = false>>
<<set $discoveredGolfRestaurant = false>>
<<set $discoveredGolfSpa = false>>
<<set $discoveredStreetFifth = false>>
<<set $discoveredBarWine = false>>
<<set $discoveredBoutiqueA = false>>
<<set $discoveredBoutiqueB = false>>
<<set $discoveredBoutiqueC = false>>
<<set $discoveredRestaurantLuxury = false>>
<<set $discoveredSalonBeautyFifth = false>>
/* ==========================================
MARINA BAY - Needs Discovery
========================================== */
<<set $discoveredMarinaBay = false>>
<<set $discoveredMarina = false>>
<<set $discoveredBeach = false>>
<<set $discoveredBeachBar = false>>
<<set $discoveredBeachClub = false>>
<<set $discoveredBeachVolleyball = false>>
<<set $discoveredPier = false>>
<<set $discoveredPierIcecream = false>>
<<set $discoveredRestaurantBeach = false>>
/* ==========================================
OLD TOWN - Needs Discovery
========================================== */
<<set $discoveredOldTown = false>>
<<set $discoveredCivicCenter = false>>
<<set $discoveredTownHall = false>>
<<set $discoveredHighSchool = false>>
<<set $discoveredLibraryOldtown = false>>
<<set $discoveredPharmacy = false>>
<<set $discoveredPoliceStation = false>>
<<set $discoveredPostOffice = false>>
<<set $discoveredDinerRubys = false>>
<<set $discoveredShopBarber = false>>
<<set $discoveredShopCoffeeOldtown = false>>
<<set $discoveredStoreBook = false>>
<<set $discoveredStoreHardware = false>>
/* ==========================================
UNIVERSITY DISTRICT - Needs Discovery
========================================== */
<<set $discoveredUniversityDistrict = false>>
<<set $discoveredCampus = false>>
<<set $discoveredCafeteriaUni = false>>
<<set $discoveredDormsUni = false>>
<<set $discoveredFratHouse = false>>
<<set $discoveredLectureHall = false>>
<<set $discoveredLibraryUni = false>>
<<set $discoveredSororityHouse = false>>
<<set $discoveredStudentBar = false>>
/* ==========================================
SOUTHSIDE - Needs Discovery
========================================== */
<<set $discoveredSouthside = false>>
<<set $discoveredApartmentComplex = false>>
<<set $discoveredCornerBlock = false>>
<<set $discoveredLaundromat = false>>
<<set $discoveredOldFactory = false>>
<<set $discoveredShopPawn = false>>
<<set $discoveredStoreLiquor = false>>
/* ==========================================
SUBURBS - Needs Discovery
========================================== */
<<set $discoveredSuburbs = false>>
<<set $discoveredApartmentsSuburbs = false>>
<<set $discoveredHouseCrack = false>>
<<set $discoveredMotelSuburbs = false>>
<<set $discoveredThePit = false>>
<<set $discoveredGangTerritory = false>>
<<set $discoveredDealerCorner = false>>
<<set $discoveredWallGraffiti = false>>
/* ==========================================
RED LIGHT CENTER - Needs Discovery
========================================== */
<<set $discoveredRedLightCenter = false>>
<<set $discoveredAlleyBack = false>>
<<set $discoveredBarGloryHole = false>>
<<set $discoveredBarNeon = false>>
<<set $discoveredBasementEntrance = false>>
<<set $discoveredClubPrivate = false>>
<<set $discoveredClubStrip = false>>
<<set $discoveredMotelRedLight = false>>
<<set $discoveredParlorMassage = false>>
<<set $discoveredRoomsPrivate = false>>
<<set $discoveredStoreAdult = false>>
<</nobr>>/* =============================================================================
FOTOGRAM – GLOBAL SETTINGS
=============================================================================
This file stores global Fotogram configuration values.
DM text pools and flow nodes are defined in dedicated DM data files.
============================================================================= */
/* Post / feed */
<<set setup.fotogramNotPostableFlags = ["topless", "nude", "explicit"]>>
<<set setup.fotogramCommentLikeRatio = 0.08>>
<<set setup.fotogramPostCooldownDefaultDays = 3>>
<<set setup.fotogramPostCooldownMinDays = 2>>
<<set setup.fotogramPostCooldownMaxDays = 4>>
<<set setup.fotogramHourlyWindowHours = 3>>
<<set setup.fotogramHourlyShare = 0.4>>
<<set setup.fotogramDailyShare = 0.6>>
<<set setup.fotogramActiveDaysMin = 3>>
<<set setup.fotogramActiveDaysMax = 5>>
/* DM profile / thresholds */
<<set setup.fotogramDmAgeMin = 18>>
<<set setup.fotogramDmAgeMax = 60>>
<<set setup.fotogramDmMaleWeight = 0.7>>
<<set setup.fotogramDmFemaleWeight = 0.3>>
<<set setup.fotogramAnonSkinTones = ["white", "black"]>>
<<set setup.fotogramDmInteractiveMinFollowers = 1000>>
<<set setup.fotogramDmInteractiveMinQuality = 50>>
<<set setup.fotogramDmMinConfidenceInteractive = 0>>
<<set setup.fotogramDmMinCorruptionSwap = 0>>
/* DM interactive – photo satisfied / want more / ask number chances (0–1) */
<<run setup.fdmPhotoChances = {
satisfiedFirst: 0.35, /* chance anon is satisfied (came) after first photo */
satisfiedSecond: 0.6, /* chance anon is satisfied after second+ photo */
askNumberChance: 0.5 /* when satisfied, chance anon asks for number swap */
}>>
/* DM behavior is configured in dedicated DM files. */
/* DM interactive runtime state defaults (used by fdmMakeState in JS). */
<<run setup.fdmStateDefaults = {
turn: 0,
lastMsgType: 'opener',
lastMsgChoices: null,
lastAnonText: '',
currentChoices: null,
awaitingPhoto: false,
ended: false,
numberAsked: false,
numberRejected: false,
photoCooldownTurns: 0,
lastPlayerDelta: 0,
pushbackStreak: 0,
pressureDebt: 0,
lastPressureTurn: -99,
anonMsgUseCount: {},
recentAnonTypes: [],
recentAnon: [],
recentPlayer: [],
recentlyOfferedChoiceTexts: [],
playerPhotoCount: 0,
anonPhotoCount: 0,
usedMediaPaths: {},
usedMediaPathsInit: false,
lastAnonAttachmentCategory: '',
alreadySentStreak: 0,
mediaFlowIndex: 0,
mediaFlowCompleted: false,
lastPlayerPhotoCountForAnonMedia: -1,
mildPhotoStreak: 0
}>>
<<script>>
setup.fdmMakeState = function (dm) {
var persona = (dm && dm.interactiveProfile && dm.interactiveProfile.persona)
? dm.interactiveProfile.persona
: "lustful";
var personas = (setup && setup.fdmPersonas) ? setup.fdmPersonas : {};
var cfg = personas[persona] || {};
var defaults = (setup && setup.fdmStateDefaults && typeof setup.fdmStateDefaults === "object")
? JSON.parse(JSON.stringify(setup.fdmStateDefaults))
: {};
var state = Object.assign({}, defaults);
state.persona = persona;
state.gender = String(cfg.gender || "male");
state.heat = Number.isFinite(Number(cfg.startHeat)) ? Number(cfg.startHeat) : 20;
if (state.heat < 0) state.heat = 0;
if (state.heat > 100) state.heat = 100;
state.heatMult = Number(cfg.heatMult != null ? cfg.heatMult : 1.0);
state.persistence = Number(cfg.persistence != null ? cfg.persistence : 3);
return state;
};
<</script>><<nobr>>
<<set setup.imageProfile = "assets/content/people/player/body/profile/playerProfile.webp">>
<<set setup.playerAppearanceImages = {
/* Hair Length Options (default straight) */
hairShort: "assets/content/people/player/body/hair/straight/short_straight.webp",
hairMedium: "assets/content/people/player/body/hair/straight/medium_straight.webp",
hairLong: "assets/content/people/player/body/hair/straight/long_straight.webp",
hairVeryLong: "assets/content/people/player/body/hair/straight/verylong_straight.webp",
/* Hair Style Options - Short */
hairShortStraight: "assets/content/people/player/body/hair/straight/short_straight.webp",
hairShortWavy: "assets/content/people/player/body/hair/wavy/short_wavy.webp",
hairShortCurly: "assets/content/people/player/body/hair/curly/short_curly.webp",
/* Hair Style Options - Medium */
hairMediumStraight: "assets/content/people/player/body/hair/straight/medium_straight.webp",
hairMediumWavy: "assets/content/people/player/body/hair/wavy/medium_wavy.webp",
hairMediumCurly: "assets/content/people/player/body/hair/curly/medium_curly.webp",
/* Hair Style Options - Long */
hairLongStraight: "assets/content/people/player/body/hair/straight/long_straight.webp",
hairLongWavy: "assets/content/people/player/body/hair/wavy/long_wavy.webp",
hairLongCurly: "assets/content/people/player/body/hair/curly/long_curly.webp",
/* Hair Style Options - Very Long */
hairVeryLongStraight: "assets/content/people/player/body/hair/straight/verylong_straight.webp",
hairVeryLongWavy: "assets/content/people/player/body/hair/wavy/verylong_wavy.webp",
hairVeryLongCurly: "assets/content/people/player/body/hair/curly/verylong_curly.webp",
/* Bust Size Options */
bustA: "assets/content/people/player/body/boobs/a-cup.webp",
bustB: "assets/content/people/player/body/boobs/b-cup.webp",
bustC: "assets/content/people/player/body/boobs/c-cup.webp",
bustD: "assets/content/people/player/body/boobs/d-cup.webp",
bustDD: "assets/content/people/player/body/boobs/dd-cup.webp",
/* Hip Size Options */
hipSlim: "assets/content/people/player/body/ass/slim.webp",
hipMedium: "assets/content/people/player/body/ass/medium.webp",
hipWide: "assets/content/people/player/body/ass/wide.webp",
hipCurvy: "assets/content/people/player/body/ass/curvy.webp",
/* Body Type Options */
bodySlim: "assets/content/people/player/body/type/slim.webp",
bodyNormal: "assets/content/people/player/body/type/normal.webp",
bodyAthletic: "assets/content/people/player/body/type/athletic.webp",
bodyCurvy: "assets/content/people/player/body/type/curvy.webp",
bodyThick: "assets/content/people/player/body/type/thick.webp",
bodyChubby: "assets/content/people/player/body/type/chubby.webp"
}>>
<</nobr>>/* ==========================================
LOCATION IMAGES - Central Repository
All location background image paths
Matches: sugarcube_passages/Locations/ structure
Total: 121 locations
========================================== */
<<set setup.locationImages = {
/* ==========================================
DOWNTOWN - City Center (37 locations)
========================================== */
"downTown": "assets/content/locations/downTown/downTown.webp",
"cityHall": "assets/content/locations/downTown/cityHall.webp",
"nightClub": "assets/content/locations/downTown/nightClub.webp",
"restaurantDowntown": "assets/content/locations/downTown/restaurantDowntown.webp",
"mall": "assets/content/locations/downTown/mall.webp",
/* -- downTown/mall -- */
"floorGround": "assets/content/locations/downTown/mall/floorGround.webp",
"floorSecond": "assets/content/locations/downTown/mall/floorSecond.webp",
"floorThird": "assets/content/locations/downTown/mall/floorThird.webp",
/* -- downTown/mall/floorGround -- */
"storeClothingA": "assets/content/locations/downTown/mall/floorGround/storeClothingA.webp",
"storeCosmetics": "assets/content/locations/downTown/mall/floorGround/storeCosmetics.webp",
"storeElectronics": "assets/content/locations/downTown/mall/floorGround/storeElectronics.webp",
"storeShoeA": "assets/content/locations/downTown/mall/floorGround/storeShoeA.webp",
"storeSports": "assets/content/locations/downTown/mall/floorGround/storeSports.webp",
"storeSwim": "assets/content/locations/downTown/mall/floorGround/storeSwim.webp",
"mallBathroom": "assets/content/locations/downTown/mall/mallBathroom.webp",
"mallBathroomFromGround": "assets/content/locations/downTown/mall/mallBathroom.webp",
"mallBathroomFromSecond": "assets/content/locations/downTown/mall/mallBathroom.webp",
"mallBathroomFromThird": "assets/content/locations/downTown/mall/mallBathroom.webp",
/* -- downTown/mall/floorSecond -- */
"storeBags": "assets/content/locations/downTown/mall/floorSecond/storeBags.webp",
"storeClothingB": "assets/content/locations/downTown/mall/floorSecond/storeClothingB.webp",
"storeClothingC": "assets/content/locations/downTown/mall/floorSecond/storeClothingC.webp",
"storeJewelry": "assets/content/locations/downTown/mall/floorSecond/storeJewelry.webp",
"storeLingerieA": "assets/content/locations/downTown/mall/floorSecond/storeLingerieA.webp",
"storeLingerieB": "assets/content/locations/downTown/mall/floorSecond/storeLingerieB.webp",
"storeShoeB": "assets/content/locations/downTown/mall/floorSecond/storeShoeB.webp",
/* -- downTown/mall/floorThird -- */
"foodCourtMall": "assets/content/locations/downTown/mall/floorThird/foodCourtMall.webp",
"salonBeautyMall": "assets/content/locations/downTown/mall/floorThird/salonBeautyMall.webp",
"theatreMovie": "assets/content/locations/downTown/mall/floorThird/theatreMovie.webp",
/* -- downTown/skyline -- */
"skyline": "assets/content/locations/downTown/skyline.webp",
/* -- downTown/towerA -- */
"towerA": "assets/content/locations/downTown/skyline/towerA.webp",
"recreationCenter": "assets/content/locations/downTown/skyline/towerA/recreationCenter.webp",
"courtBasketball": "assets/content/locations/downTown/skyline/towerA/recreationCenter/courtBasketball.webp",
"courtVolleyball": "assets/content/locations/downTown/skyline/towerA/recreationCenter/courtVolleyball.webp",
"gym": "assets/content/locations/downTown/skyline/towerA/recreationCenter/gym.webp",
"gymMaleLocker": "assets/content/locations/downTown/skyline/towerA/recreationCenter/gym.webp",
"gymMaleLockerShower": "assets/content/locations/maplewood/FamilyHouse/fhUpperBath.webp",
"gymFemaleLocker": "assets/content/locations/downTown/skyline/towerA/recreationCenter/gym.webp",
"gymFemaleLockerWardrobe": "assets/content/locations/maplewood/FamilyHouse/fhWardrobe.webp",
/* -- downTown/towerB -- */
"towerB": "assets/content/locations/downTown/skyline/towerB.webp",
"bank": "assets/content/locations/downTown/skyline/towerB/bank.webp",
/* -- downTown/towerC -- */
"towerC": "assets/content/locations/downTown/skyline/towerC.webp",
"hotel": "assets/content/locations/downTown/skyline/towerC/hotel.webp",
"jazzClub": "assets/content/locations/downTown/skyline/towerC/jazzClub.webp",
"pethouseSuite": "assets/content/locations/downTown/skyline/towerC/pethouseSuite.webp",
"restaurantRoof": "assets/content/locations/downTown/skyline/towerC/restaurantRoof.webp",
"roofTopLounge": "assets/content/locations/downTown/skyline/towerC/roofTopLounge.webp",
/* ==========================================
HILLCREST - Upscale District (13 locations)
========================================== */
"hillcrest": "assets/content/locations/hillcrest/hillcrest.webp",
"galleryArt": "assets/content/locations/hillcrest/galleryArt.webp",
"streetFifth": "assets/content/locations/hillcrest/streetFifth.webp",
"golfClub": "assets/content/locations/hillcrest/golfClub.webp",
/* -- hillcrest/golfClub -- */
"golfCourse": "assets/content/locations/hillcrest/golfClub/golfCourse.webp",
"golfRestaurant": "assets/content/locations/hillcrest/golfClub/golfRestaurant.webp",
"golfSpa": "assets/content/locations/hillcrest/golfClub/golfSpa.webp",
/* -- hillcrest/streetFifth -- */
"barWine": "assets/content/locations/hillcrest/streetFifth/barWine.webp",
"boutiqueA": "assets/content/locations/hillcrest/streetFifth/boutiqueA.webp",
"boutiqueB": "assets/content/locations/hillcrest/streetFifth/boutiqueB.webp",
"boutiqueC": "assets/content/locations/hillcrest/streetFifth/boutiqueC.webp",
"restaurantLuxury": "assets/content/locations/hillcrest/streetFifth/restaurantLuxury.webp",
"salonBeautyFifth": "assets/content/locations/hillcrest/streetFifth/salonBeautyFifth.webp",
/* ==========================================
MAPLEWOOD - Residential District (15 locations)
========================================== */
"maplewood": "assets/content/locations/maplewood/maplewood.webp",
"mapleChurch": "assets/content/locations/maplewood/mapleChurch.webp",
"storeCorner": "assets/content/locations/maplewood/storeCorner.webp",
"sunsetPark": "assets/content/locations/maplewood/sunsetPark.webp",
"sunsetWC": "assets/content/locations/maplewood/sunsetPark/sunsetWC.webp",
/* -- maplewood/FamilyHouse -- */
"FamilyHouse": "assets/content/locations/maplewood/familyHouse.webp",
"fhUpperstairs": "assets/content/locations/maplewood/FamilyHouse/fhUpperstairs.webp",
"fhBackyard": "assets/content/locations/maplewood/FamilyHouse/fhBackyard.webp",
"fhBedroom": "assets/content/locations/maplewood/FamilyHouse/fhBedroom.webp",
"fhBrotherRoom": "assets/content/locations/maplewood/FamilyHouse/fhBrotherRoom.webp",
"fhBrotherRoomPC": "assets/content/scenes/maplewood/familyHouse/brotherRoom/brotherPC.webp",
"fhDownbath": "assets/content/locations/maplewood/FamilyHouse/fhDownBath.webp",
"fhDownstairs": "assets/content/locations/maplewood/FamilyHouse/fhDownstairs.webp",
"fhKitchen": "assets/content/locations/maplewood/FamilyHouse/fhKitchen.webp",
"fhLivingroom": "assets/content/locations/maplewood/FamilyHouse/fhLivingroom.webp",
"fhParentsBath": "assets/content/locations/maplewood/FamilyHouse/fhParentsBath.webp",
"fhParentsRoom": "assets/content/locations/maplewood/FamilyHouse/fhParentsRoom.webp",
"fhUpperBath": "assets/content/locations/maplewood/FamilyHouse/fhUpperBath.webp",
"fhLaundryRoom": "assets/content/locations/maplewood/FamilyHouse/fhLaundryRoom.webp",
"fhWardrobe": "assets/content/locations/maplewood/FamilyHouse/fhWardrobe.webp",
"fhGarage": "assets/content/locations/maplewood/FamilyHouse/fhGarage.webp",
/* ==========================================
MARINA BAY - Waterfront District (9 locations)
========================================== */
"marinaBay": "assets/content/locations/marinaBay/marinaBay.webp",
"marina": "assets/content/locations/marinaBay/marina.webp",
"beachBar": "assets/content/locations/marinaBay/beachBar.webp",
"beachClub": "assets/content/locations/marinaBay/beachClub.webp",
"restaurantBeach": "assets/content/locations/marinaBay/restaurantBeach.webp",
"beach": "assets/content/locations/marinaBay/beach.webp",
"pier": "assets/content/locations/marinaBay/pier.webp",
/* -- marinaBay/beach -- */
"beachVolleyball": "assets/content/locations/marinaBay/beach/beachVolleyball.webp",
/* -- marinaBay/pier -- */
"pierIcecream": "assets/content/locations/marinaBay/pier/pierIcecream.webp",
/* ==========================================
OLD TOWN - Historic District (12 locations)
========================================== */
"oldTown": "assets/content/locations/oldTown/oldTown.webp",
"civicCenter": "assets/content/locations/oldTown/civicCenter.webp",
"townHall": "assets/content/locations/oldTown/civicCenter/townHall.webp",
"highSchool": "assets/content/locations/oldTown/civicCenter/highSchool.webp",
"libraryOldtown": "assets/content/locations/oldTown/civicCenter/libraryOldtown.webp",
"hospital": "assets/content/locations/oldTown/civicCenter/hospital.webp",
"pharmacy": "assets/content/locations/oldTown/pharmacy.webp",
"policeStation": "assets/content/locations/oldTown/civicCenter/policeStation.webp",
"postOffice": "assets/content/locations/oldTown/civicCenter/postOffice.webp",
"dinerRubys": "assets/content/locations/oldTown/dinerRubys.webp",
"dinerRubysBathroom": "assets/content/locations/oldTown/dinerRubys/dinerBathroom.webp",
"dinerRubysKitchen": "assets/content/locations/oldTown/dinerRubys/dinerKitchen.webp",
"dinerRubysCorridor": "assets/content/locations/oldTown/dinerRubys/dinerCorridor.webp",
"dinerRubysManagerOffice": "assets/content/locations/oldTown/dinerRubys/dinerManager.webp",
"dinerRubysDressingRoom": "assets/content/locations/oldTown/dinerRubys/dinerDressingRoom.webp",
"dinerRubysStorage": "assets/content/locations/oldTown/dinerRubys/dinerStorage.webp",
"shopBarber": "assets/content/locations/oldTown/shopBarber.webp",
"shopCoffeeOldtown": "assets/content/locations/oldTown/shopCoffeeOldtown.webp",
"storeBook": "assets/content/locations/oldTown/storeBook.webp",
"storeHardware": "assets/content/locations/oldTown/storeHardware.webp",
/* ==========================================
UNIVERSITY DISTRICT - Campus Area (9 locations)
========================================== */
"universityDistrict": "assets/content/locations/universityDistrict/universityDistrict.webp",
"campus": "assets/content/locations/universityDistrict/campus.webp",
"cafeteriaUni": "assets/content/locations/universityDistrict/cafeteriaUni.webp",
"dormsUni": "assets/content/locations/universityDistrict/dormsUni.webp",
"fratHouse": "assets/content/locations/universityDistrict/fratHouse.webp",
"lectureHall": "assets/content/locations/universityDistrict/lectureHall.webp",
"libraryUni": "assets/content/locations/universityDistrict/libraryUni.webp",
"sororityHouse": "assets/content/locations/universityDistrict/sororityHouse.webp",
"studentBar": "assets/content/locations/universityDistrict/studentBar.webp",
/* ==========================================
SOUTHSIDE - Industrial/Lower Income (7 locations)
========================================== */
"southside": "assets/content/locations/southside/southside.webp",
"apartmentComplex": "assets/content/locations/southside/apartmentComplex.webp",
"cornerBlock": "assets/content/locations/southside/cornerBlock.webp",
"laundromat": "assets/content/locations/southside/laundromat.webp",
"oldFactory": "assets/content/locations/southside/oldFactory.webp",
"shopPawn": "assets/content/locations/southside/shopPawn.webp",
"storeLiquor": "assets/content/locations/southside/storeLiquor.webp",
/* ==========================================
SUBURBS - Outer Residential (8 locations)
========================================== */
"suburbs": "assets/content/locations/suburbs/suburbs.webp",
"apartmentsSuburbs": "assets/content/locations/suburbs/apartmentsSuburbs.webp",
"dealerCorner": "assets/content/locations/suburbs/dealerCorner.webp",
"motelSuburbs": "assets/content/locations/suburbs/motelSuburbs.webp",
"thePit": "assets/content/locations/suburbs/thePit.webp",
"gangTerritory": "assets/content/locations/suburbs/gangTerritory.webp",
/* -- suburbs/gangTerritory -- */
"wallGraffiti": "assets/content/locations/suburbs/gangTerritory/wallGraffiti.webp",
"houseCrack": "assets/content/locations/suburbs/gangTerritory/houseCrack.webp",
/* ==========================================
RED LIGHT CENTER - Adult District (11 locations)
========================================== */
"redLightCenter": "assets/content/locations/redLightCenter/redLightCenter.webp",
"alleyBack": "assets/content/locations/redLightCenter/alleyBack.webp",
"basementEntrance": "assets/content/locations/redLightCenter/basementEntrance.webp",
"barGloryHole": "assets/content/locations/redLightCenter/basement/barGloryHole.webp",
"barNeon": "assets/content/locations/redLightCenter/basement/barNeon.webp",
"clubStrip": "assets/content/locations/redLightCenter/basement/clubStrip.webp",
"clubPrivate": "assets/content/locations/redLightCenter/clubPrivate.webp",
"motelRedLight": "assets/content/locations/redLightCenter/motelRedLight.webp",
"parlorMassage": "assets/content/locations/redLightCenter/parlorMassage.webp",
"roomsPrivate": "assets/content/locations/redLightCenter/roomsPrivate.webp",
"storeAdult": "assets/content/locations/redLightCenter/storeAdult.webp"
}>>/* ==========================================
MAP REGIONS - Main Districts (Map-Specific Data Only)
Other data pulled from: setup.navCards, setup.locationImages
========================================== */
<<set setup.mapRegions = [
{
id: "maplewood",
mapImage: "assets/content/locations/map/maplewood.webp",
desc: "Neighborhood - Your home",
color: "#22c55e",
x: 55,
y: 15
},
{
id: "downTown",
mapImage: "assets/content/locations/map/downtown.webp",
desc: "City center - Business and entertainment",
color: "#3b82f6",
x: 55,
y: 45
},
{
id: "hillcrest",
mapImage: "assets/content/locations/map/hillcrest.webp",
desc: "Upscale district - Luxury and exclusivity",
color: "#a855f7",
x: 93,
y: 60
},
{
id: "marinaBay",
mapImage: "assets/content/locations/map/marinaBay.webp",
desc: "Waterfront district - Beach and pier",
color: "#06b6d4",
x: 38,
y: 80
},
{
id: "oldTown",
mapImage: "assets/content/locations/map/oldTown.webp",
desc: "Historic district - Traditional shops and school",
color: "#f59e0b",
x: 70,
y: 60
},
{
id: "universityDistrict",
mapImage: "assets/content/locations/map/universityDistrict.webp",
desc: "College area - Students and academia",
color: "#ef4444",
x: 83,
y: 90
},
{
id: "southside",
mapImage: "assets/content/locations/map/southside.webp",
desc: "Working class area - Industrial and tough",
color: "#71717a",
x: 15,
y: 50
},
{
id: "suburbs",
mapImage: "assets/content/locations/map/suburbs.webp",
desc: "Outskirts - Cheaper but rougher",
color: "#a3a3a3",
x: 5,
y: 15
},
{
id: "redLightCenter",
mapImage: "assets/content/locations/map/redLightCenter.webp",
desc: "Adult entertainment district",
color: "#ec4899",
x: 25,
y: 20
}
] >>
/* ==========================================
MAP LOCATIONS - Map-Specific Data Only
structureType: area (show children as cards), floor (mall floors), residence (show residents), location (default)
category: business, store, food, entertainment, public
========================================== */
<<set setup.mapLocations = {
/* ==========================================
MAPLEWOOD
========================================== */
FamilyHouse: {
structureType: "residence",
category: "Residential",
x: 8,
y: 45,
residents: ["mother", "father", "brother"],
taxiCost: 0
},
storeCorner: {
structureType: "location",
category: "Store",
residents: ["shopClerkMarcus"],
x: 25,
y: 15,
taxiCost: 5
},
mapleChurch: {
structureType: "location",
category: "Public",
x: 50,
y: 50,
taxiCost: 5
},
sunsetPark: {
structureType: "location",
category: "Public",
x: 40,
y: 30,
taxiCost: 5
},
/* ==========================================
DOWNTOWN
========================================== */
skyline: {
structureType: "area",
category: "Business",
x: 15,
y: 20,
taxiCost: 15
},
/* -- Mall & Floors -- */
mall: {
structureType: "area",
category: "Store",
x: 75,
y: 45,
taxiCost: 15
},
floorGround: {
structureType: "floor",
category: "Store",
x: 46,
y: 55,
taxiCost: 15
},
floorSecond: {
structureType: "floor",
category: "Store",
x: 46,
y: 40,
taxiCost: 15
},
floorThird: {
structureType: "floor",
category: "Store",
x: 46,
y: 25,
taxiCost: 15
},
/* -- Downtown Direct -- */
cityHall: {
structureType: "location",
category: "Public",
x: 70,
y: 10,
taxiCost: 15
},
nightClub: {
structureType: "location",
category: "Entertainment",
x: 30,
y: 60,
taxiCost: 15
},
restaurantDowntown: {
structureType: "location",
category: "Food",
x: 70,
y: 80,
taxiCost: 15
},
/* -- Skyline Towers -- */
towerA: {
structureType: "area",
category: "Business",
x: 45,
y: 35,
taxiCost: 15
},
recreationCenter: {
structureType: "area",
category: "Entertainment",
x: 40,
y: 80,
taxiCost: 15
},
towerB: {
structureType: "area",
category: "Business",
x: 32,
y: 30,
taxiCost: 15
},
towerC: {
structureType: "area",
category: "Business",
x: 70,
y: 30,
taxiCost: 20
},
/* ==========================================
HILLCREST
========================================== */
galleryArt: {
structureType: "location",
category: "Entertainment",
x: 30,
y: 10,
taxiCost: 20
},
golfClub: {
structureType: "area",
category: "Entertainment",
x: 65,
y: 15,
taxiCost: 25
},
streetFifth: {
structureType: "area",
category: "Store",
x: 45,
y: 50,
taxiCost: 20
},
/* ==========================================
MARINA BAY
========================================== */
marina: {
structureType: "location",
category: "Business",
x: 55,
y: 65,
taxiCost: 18
},
beachClub: {
structureType: "location",
category: "Entertainment",
x: 40,
y: 80,
taxiCost: 22
},
restaurantBeach: {
structureType: "location",
category: "Food",
x: 25,
y: 25,
taxiCost: 18
},
beach: {
structureType: "area",
category: "Public",
x: 30,
y: 40,
taxiCost: 18
},
pier: {
structureType: "area",
category: "Public",
x: 35,
y: 60,
taxiCost: 18
},
/* ==========================================
OLD TOWN
========================================== */
civicCenter: {
structureType: "location",
category: "Public",
x: 80,
y: 25,
taxiCost: 12
},
pharmacy: {
structureType: "location",
category: "Store",
x: 55,
y: 65,
taxiCost: 12
},
dinerRubys: {
structureType: "residence",
category: "Food",
x: 55,
y: 15,
taxiCost: 12,
residents: ["dinerManager", "dinerClerk", "dinerChef", "dinerDishwasher", "dinerWaitress1", "dinerWaitress2", "dinerWaitress3"]
},
shopBarber: {
structureType: "location",
category: "Store",
x: 50,
y: 40,
taxiCost: 12
},
shopCoffeeOldtown: {
structureType: "location",
category: "Food",
x: 15,
y: 55,
taxiCost: 12
},
storeBook: {
structureType: "location",
category: "Store",
x: 25,
y: 35,
taxiCost: 12
},
storeHardware: {
structureType: "location",
category: "Store",
x: 85,
y: 60,
taxiCost: 12
},
/* ==========================================
UNIVERSITY DISTRICT
========================================== */
campus: {
structureType: "location",
category: "Public",
x: 55,
y: 55,
taxiCost: 14
},
dormsUni: {
structureType: "residence",
category: "Residential",
x: 55,
y: 30,
taxiCost: 14
},
studentBar: {
structureType: "location",
category: "Food",
x: 75,
y: 55,
taxiCost: 14
},
fratHouse: {
structureType: "residence",
category: "Residential",
x: 40,
y: 65,
taxiCost: 14
},
sororityHouse: {
structureType: "residence",
category: "Residential",
x: 60,
y: 80,
taxiCost: 14
},
/* ==========================================
SOUTHSIDE
========================================== */
apartmentComplex: {
structureType: "residence",
category: "Residential",
x: 40,
y: 65,
taxiCost: 10
},
cornerBlock: {
structureType: "area",
category: "Public",
x: 15,
y: 25,
taxiCost: 10
},
laundromat: {
structureType: "location",
category: "Store",
x: 65,
y: 40,
taxiCost: 10
},
oldFactory: {
structureType: "location",
category: "Public",
x: 50,
y: 10,
taxiCost: 10
},
shopPawn: {
structureType: "location",
category: "Store",
x: 55,
y: 85,
taxiCost: 10
},
storeLiquor: {
structureType: "location",
category: "Store",
x: 40,
y: 35,
taxiCost: 10
},
/* ==========================================
SUBURBS
========================================== */
apartmentsSuburbs: {
structureType: "residence",
category: "Residential",
x: 85,
y: 40,
taxiCost: 8
},
houseCrack: {
structureType: "residence",
category: "Residential",
x: 50,
y: 40,
taxiCost: 8
},
motelSuburbs: {
structureType: "location",
category: "Business",
x: 70,
y: 10,
taxiCost: 8
},
thePit: {
structureType: "location",
category: "Entertainment",
x: 35,
y: 65,
taxiCost: 8
},
gangTerritory: {
structureType: "location",
category: "Public",
x: 20,
y: 20,
taxiCost: 8
},
/* ==========================================
RED LIGHT CENTER
========================================== */
alleyBack: {
structureType: "area",
category: "Public",
x: 10,
y: 20,
taxiCost: 15
},
basementEntrance: {
structureType: "location",
category: "Entertainment",
x: 35,
y: 45,
taxiCost: 15
},
roomsPrivate: {
structureType: "location",
category: "Entertainment",
x: 35,
y: 20,
taxiCost: 15
},
clubPrivate: {
structureType: "location",
category: "Entertainment",
x: 60,
y: 40,
taxiCost: 15
},
motelRedLight: {
structureType: "location",
category: "Business",
x: 55,
y: 80,
taxiCost: 15
},
parlorMassage: {
structureType: "location",
category: "Business",
x: 15,
y: 70,
taxiCost: 15
},
storeAdult: {
structureType: "location",
category: "Store",
x: 68,
y: 13,
taxiCost: 15,
}
} >>
/* ==========================================
LOCATION HOURS
Simple format: { open: X, close: Y, region: "regionId" }
========================================== */
<<set setup.locationHours = {
/* === DOWNTOWN === */
/* Mall Start*/
mall: { open: 10, close: 22, region: "downTown" },
floorGround: { open: 10, close: 22, region: "downTown" },
floorSecond: { open: 10, close: 22, region: "downTown" },
floorThird: { open: 10, close: 22, region: "downTown" },
storeClothingA: { open: 10, close: 22, region: "downTown" },
storeClothingB: { open: 10, close: 22, region: "downTown" },
storeClothingC: { open: 10, close: 22, region: "downTown" },
storeShoeA: { open: 10, close: 22, region: "downTown" },
storeShoeB: { open: 10, close: 22, region: "downTown" },
storeLingerieA: { open: 10, close: 22, region: "downTown" },
storeLingerieB: { open: 10, close: 22, region: "downTown" },
storeCosmetics: { open: 10, close: 22, region: "downTown" },
storeJewelry: { open: 10, close: 22, region: "downTown" },
storeBags: { open: 10, close: 22, region: "downTown" },
storeElectronics: { open: 10, close: 22, region: "downTown" },
storeSports: { open: 10, close: 22, region: "downTown" },
foodCourtMall: { open: 10, close: 22, region: "downTown" },
theatreMovie: { open: 10, close: 22, region: "downTown" },
salonBeautyMall: { open: 10, close: 22, region: "downTown" },
/* Mall End*/
/* Downtown Start*/
cityHall: { open: 8, close: 17, region: "downTown" },
restaurantDowntown: { open: 12, close: 2, region: "downTown" },
nightClub: { open: 22, close: 5, region: "downTown" },
/* Downtown End*/
/*Skyline Start*/
towerA: { open: 8, close: 22, region: "downTown" },
recreationCenter: { open: 8, close: 22, region: "downTown" },
gym: { open: 8, close: 22, region: "downTown" },
courtBasketball: { open: 8, close: 22, region: "downTown" },
courtVolleyball: { open: 8, close: 22, region: "downTown" },
towerB: { open: 8, close: 17, region: "downTown" },
bank: { open: 8, close: 17, region: "downTown" },
towerC: { open24h: true },
hotel: { open24h: true },
jazzClub: { open: 14, close: 2, region: "downTown" },
restaurantRoofTop: { open: 12, close: 2, region: "downTown" },
roofTopLounge: { open: 12, close: 2, region: "downTown" },
/*Skyline End*/
/* === OLD TOWN === */
/* civicCenter Start */
townHall: { open: 8, close: 17, region: "oldTown" },
postOffice: { open: 8, close: 17, region: "oldTown" },
highSchool: { open: 8, close: 17, region: "oldTown" },
libraryOldtown: { open: 8, close: 17, region: "oldTown" },
hospital: { open24h: true },
policeStation: { open24h: true },
/* civicCenter End */
pharmacy: { open: 9, close: 21, region: "oldTown" },
dinerRubys: { open: 6, close: 22, region: "oldTown" },
shopCoffeeOldtown: { open: 6, close: 20, region: "oldTown" },
storeBook: { open: 9, close: 21, region: "oldTown" },
storeHardware: { open: 9, close: 21, region: "oldTown" },
shopBarber: { open: 9, close: 21, region: "oldTown" },
/* === MARINA BAY === */
beach: { open: 6, close: 22, region: "marinaBay" },
pier: { open: 6, close: 23, region: "marinaBay" },
beachClub: { open: 10, close: 2, region: "marinaBay" },
/* === UNIVERSITY === */
studentBar: { open: 16, close: 2, region: "universityDistrict" },
cafeteriaUni: { open: 7, close: 21, region: "universityDistrict" },
/* === RED LIGHT === */
clubStrip: { open: 21, close: 4, region: "redLightCenter" },
barNeon: { open: 20, close: 4, region: "redLightCenter" },
barGloryHole: { open: 22, close: 5, region: "redLightCenter" },
storeAdult: { open: 10, close: 2, region: "redLightCenter" },
/* === MAPLEWOOD === */
sunsetPark: { open: 6, close: 22, region: "maplewood" },
mapleChurch: { open: 8, close: 20, region: "maplewood" },
storeCorner: { open: 9, close: 22, region: "maplewood" }
} >>
/* ==========================================
MAP SETTINGS
========================================== */
<<set $taxiBaseFare = 10>>
<<set setup.imageMap = "assets/content/locations/map/map.webp">><<script>>
/* =====================================================================
NAVIGATION SYSTEM - Location & Travel Time Configuration
=====================================================================
This file consists of 2 main sections:
1. setup.navCards - Location names and passage definitions
2. setup.locations - Location configuration (parent, type)
=====================================================================
GUIDE TO ADDING NEW LOCATIONS
=====================================================================
Example: Adding a new place called "lockerRoom" (inside gym)
STEP 1: Add to navCards
-----------------------
"lockerRoom": { name: "Locker Room" },
Camera (optional): cameraLoc = "public" | "safe" | "forbidden" | "never"
- public: public media pool. Hot/Spicy need higher corruption outside.
- safe: safe media pool. Lower corruption for Hot/Spicy.
- forbidden: no Hot/Spicy unless Exhibitionism >= cameraExhibitionismMin (then public pool).
- never: no Hot/Spicy; Normal/Cute may fall back to the global media pool.
Example: "policeStation": { name: "5th Precinct", cameraLoc: "never" }
STEP 2: Add to locations
------------------------
"lockerRoom": { parent: "gym", type: "subroom" },
=====================================================================
TRAVEL TIME RULES (Type-based)
=====================================================================
| Transition Type | Time |
|-------------------------------|--------|
| Room ↔ Room (same building) | 1 min |
| Room/Subroom ↔ Parent | 1 min |
| Building ↔ Building | 5 min |
| District ↔ District | 15 min |
===================================================================== */
setup.navCards = {
/* ==========================================
MAPLEWOOD - FAMILY HOUSE
========================================== */
"FamilyHouse": { name: "Family House", passage: "fhDownstairs", cameraLoc: "public" },
"fhBedroom": { name: "My Bedroom", cameraLoc: "safe" },
"fhWardrobe": { name: "Wardrobe", cameraLoc: "public" },
"fhBrotherRoom": { name: "Brother's Room", cameraLoc: "public" },
"fhKitchen": { name: "Kitchen", cameraLoc: "public" },
"fhLivingroom": { name: "Living Room", cameraLoc: "public" },
"fhUpperBath": { name: "Upstairs Bathroom", cameraLoc: "public" },
"fhLaundryRoom": { name: "Laundry Room", cameraLoc: "public", outfitContext: "indoor" },
"fhDownbath": { name: "Downstairs Bathroom", cameraLoc: "public" },
"fhUpperstairs": { name: "Upstairs", cameraLoc: "public", outfitContext: "indoor" },
"fhDownstairs": { name: "Downstairs", cameraLoc: "public" },
"fhParentsRoom": { name: "Parents Room", cameraLoc: "public" },
"fhParentsBath": { name: "Parents Bathroom", cameraLoc: "public" },
"fhBackyard": { name: "Backyard", cameraLoc: "public" },
"fhGarage": { name: "Garage", cameraLoc: "public" },
/* ==========================================
MAPLEWOOD - NEIGHBORHOOD
========================================== */
"maplewood": { name: "Maplewood", cameraLoc: "public", outfitContext: "outdoor" },
"mapleChurch": { name: "St. Mary's Chapel", cameraLoc: "forbidden", cameraExhibitionismMin: 60 },
"storeCorner": { name: "Quick Mart", cameraLoc: "public" },
"sunsetPark": { name: "Sunset Park", meetup: true, cameraLoc: "public" },
"sunsetWC": { name: "Restroom", cameraLoc: "public" },
/* ==========================================
DOWNTOWN
========================================== */
"downTown": { name: "Downtown", cameraLoc: "public" },
"bank": { name: "First National Bank", cameraLoc: "never" },
"cityHall": { name: "City Hall", cameraLoc: "never" },
"hospital": { name: "St. Luke's Hospital", cameraLoc: "forbidden", cameraExhibitionismMin: 55 },
"hotel": { name: "The Grand Hotel", cameraLoc: "public" },
"nightClub": { name: "Pulse Nightclub", cameraLoc: "public" },
"restaurantDowntown": { name: "The Brass Rail", cameraLoc: "public" },
/* -- Mall -- */
"mall": { name: "Metro Shopping Center", meetup: true, cameraLoc: "public" },
"floorGround": { name: "Ground Floor", cameraLoc: "public" },
"floorSecond": { name: "Second Floor", cameraLoc: "public" },
"floorThird": { name: "Third Floor", cameraLoc: "public" },
/* -- Mall Stores Ground -- */
"storeClothingA": { name: "Northline Apparel", cameraLoc: "public" },
"storeCosmetics": { name: "Lumière Beauty", cameraLoc: "public" },
"storeElectronics": { name: "TechPoint Express", cameraLoc: "public" },
"storeShoeA": { name: "StepUp Footwear", cameraLoc: "public" },
"storeSports": { name: "FastBreak Athletics", cameraLoc: "public" },
"storeSwim": { name: "Aqua Lane", cameraLoc: "public" },
/* -- Mall Stores Second -- */
"storeBags": { name: "Luxe Leather", cameraLoc: "public" },
"storeClothingB": { name: "VERA Mode", cameraLoc: "public" },
"storeClothingC": { name: "Fifth Avenue Wear", cameraLoc: "public" },
"storeJewelry": { name: "Diamond Dreams", cameraLoc: "public" },
"storeLingerieA": { name: "Silk & Lace", cameraLoc: "public" },
"storeLingerieB": { name: "Intimate Secrets", cameraLoc: "public" },
"storeShoeB": { name: "Stiletto Studio", cameraLoc: "public" },
/* -- Mall Bathroom (setBefore = set return floor, then go to mallBathroom - no gate in history) -- */
"mallBathroom": { name: "Bathroom", cameraLoc: "public" },
"mallBathroomFromGround": { name: "Bathroom", passage: "mallBathroom", setBefore: "$mallBathroomReturn to 'floorGround'", cameraLoc: "public" },
"mallBathroomFromSecond": { name: "Bathroom", passage: "mallBathroom", setBefore: "$mallBathroomReturn to 'floorSecond'", cameraLoc: "public" },
"mallBathroomFromThird": { name: "Bathroom", passage: "mallBathroom", setBefore: "$mallBathroomReturn to 'floorThird'", cameraLoc: "public" },
/* -- Mall Third Floor -- */
"foodCourtMall": { name: "Food Court", cameraLoc: "public" },
"salonBeautyMall": { name: "Headlines Salon", cameraLoc: "public" },
"theatreMovie": { name: "CineLux Cinemas", cameraLoc: "public" },
/* -- Skyline (Tower District) -- */
"skyline": { name: "Skyline District", cameraLoc: "public" },
"towerA": { name: "Apex Tower", cameraLoc: "public" },
"recreationCenter": { name: "Metro Recreation Center", cameraLoc: "public" },
"courtBasketball": { name: "Basketball Courts", cameraLoc: "public" },
"courtVolleyball": { name: "Volleyball Courts", cameraLoc: "public" },
"gym": { name: "Iron Works Gym", cameraLoc: "public" },
"gymMaleLocker": { name: "Men's Locker Room", cameraLoc: "public" },
"gymMaleLockerShower": { name: "Men's Showers", cameraLoc: "public" },
"gymFemaleLocker": { name: "Women's Locker Room", cameraLoc: "public" },
"gymFemaleLockerWardrobe": { name: "Locker", cameraLoc: "public" },
/* -- Tower B (Sterling Tower) -- */
"towerB": { name: "Sterling Tower", cameraLoc: "public" },
/* -- Tower C (Crown Plaza) -- */
"towerC": { name: "Crown Plaza", cameraLoc: "public" },
"jazzClub": { name: "The Blue Note", cameraLoc: "public" },
"pethouseSuite": { name: "The Pinnacle", cameraLoc: "public" },
"restaurantRoof": { name: "Altitude 47", cameraLoc: "public" },
"roofTopLounge": { name: "Cloud Nine Lounge", cameraLoc: "public" },
/* ==========================================
HILLCREST
========================================== */
"hillcrest": { name: "Hillcrest", cameraLoc: "public" },
"galleryArt": { name: "The Gallery", cameraLoc: "public" },
"golfClub": { name: "Hillcrest Country Club", cameraLoc: "public" },
"golfCourse": { name: "Hillcrest Golf Course", cameraLoc: "public" },
"golfRestaurant": { name: "The Clubhouse", cameraLoc: "public" },
"golfSpa": { name: "The Spa at Hillcrest", cameraLoc: "public" },
/* -- Fifth Street -- */
"streetFifth": { name: "Fifth Avenue", cameraLoc: "public" },
"barWine": { name: "The Wine Cellar", cameraLoc: "public" },
"boutiqueA": { name: "Maison Élise", cameraLoc: "public" },
"boutiqueB": { name: "Aurum Couture", cameraLoc: "public" },
"boutiqueC": { name: "Bellucci Milano", cameraLoc: "public" },
"restaurantLuxury": { name: "Château Noir", cameraLoc: "public" },
"salonBeautyFifth": { name: "Salon Privé", cameraLoc: "public" },
/* ==========================================
MARINA BAY
========================================== */
"marinaBay": { name: "Marina Bay", cameraLoc: "public" },
"marina": { name: "Harbor Marina", cameraLoc: "public" },
"beach": { name: "Marina Beach", cameraLoc: "public" },
"beachBar": { name: "The Sandbar", cameraLoc: "public" },
"beachClub": { name: "The Yacht Club", cameraLoc: "public" },
"beachVolleyball": { name: "Beach Volleyball Courts", cameraLoc: "public" },
"pier": { name: "The Pier", cameraLoc: "public" },
"pierIcecream": { name: "Scoops & Swirls", cameraLoc: "public" },
"restaurantBeach": { name: "Captain Jack's", cameraLoc: "public" },
/* ==========================================
OLD TOWN
========================================== */
"oldTown": { name: "Old Town", cameraLoc: "public" },
"civicCenter": { name: "Civic Center", cameraLoc: "public" },
"townHall": { name: "Town Hall", cameraLoc: "never" },
"highSchool": { name: "Lincoln High School", cameraLoc: "forbidden", cameraExhibitionismMin: 65 },
"libraryOldtown": { name: "Carnegie Public Library", cameraLoc: "public" },
"pharmacy": { name: "Green Cross Pharmacy", cameraLoc: "public" },
"policeStation": { name: "5th Precinct", cameraLoc: "never" },
"postOffice": { name: "Old Town Post Office", cameraLoc: "public" },
"dinerRubys": { name: "Ruby's Diner", meetup: true, cameraLoc: "public" },
"dinerRubysBathroom": { name: "Bathroom", cameraLoc: "public" },
"dinerRubysKitchen": { name: "Kitchen", cameraLoc: "public" },
"dinerRubysCorridor": { name: "Corridor", cameraLoc: "public" },
"dinerRubysManagerOffice": { name: "Manager's Office", cameraLoc: "public" },
"dinerRubysDressingRoom": { name: "Dressing Room", cameraLoc: "public" },
"dinerRubysStorage": { name: "Storage", cameraLoc: "public" },
"shopBarber": { name: "The Clipper Shop", cameraLoc: "public" },
"shopCoffeeOldtown": { name: "The Daily Grind", meetup: true, cameraLoc: "public" },
"storeBook": { name: "Bookworm's Corner", cameraLoc: "public" },
"storeHardware": { name: "Dan's Hardware", cameraLoc: "public" },
/* ==========================================
UNIVERSITY DISTRICT
========================================== */
"universityDistrict": { name: "University District", cameraLoc: "public" },
"campus": { name: "University Campus", cameraLoc: "public" },
"cafeteriaUni": { name: "Student Union", cameraLoc: "public" },
"dormsUni": { name: "Wellington Hall", cameraLoc: "public" },
"fratHouse": { name: "Delta Tau House", cameraLoc: "public" },
"lectureHall": { name: "Science Building", cameraLoc: "public" },
"libraryUni": { name: "Morrison Library", cameraLoc: "public" },
"sororityHouse": { name: "Alpha Omega House", cameraLoc: "public" },
"studentBar": { name: "The Tap Room", cameraLoc: "public" },
/* ==========================================
SOUTHSIDE
========================================== */
"southside": { name: "Southside", cameraLoc: "public" },
"apartmentComplex": { name: "Brookside Apartments", cameraLoc: "public" },
"cornerBlock": { name: "Junction Street", cameraLoc: "public" },
"laundromat": { name: "Suds Laundromat", cameraLoc: "public" },
"oldFactory": { name: "Abandoned Textile Mill", cameraLoc: "public" },
"shopPawn": { name: "Lucky's Pawn & Loan", cameraLoc: "public" },
"storeLiquor": { name: "Quick Liquor", cameraLoc: "public" },
/* ==========================================
SUBURBS
========================================== */
"suburbs": { name: "Suburbs", cameraLoc: "public" },
"apartmentsSuburbs": { name: "Sunset Heights", cameraLoc: "public" },
"houseCrack": { name: "Condemned House", cameraLoc: "public" },
"motelSuburbs": { name: "Route 9 Motor Lodge", cameraLoc: "public" },
"thePit": { name: "The Pit", cameraLoc: "public" },
"gangTerritory": { name: "West Side", cameraLoc: "public" },
"dealerCorner": { name: "The Drop", cameraLoc: "public" },
"wallGraffiti": { name: "The Wall", cameraLoc: "public" },
/* ==========================================
RED LIGHT CENTER
========================================== */
"redLightCenter": { name: "Red Light Center", cameraLoc: "public" },
"alleyBack": { name: "Back Alley", cameraLoc: "public" },
"barGloryHole": { name: "Anonymous", cameraLoc: "public" },
"barNeon": { name: "Neon Nights", cameraLoc: "public" },
"basementEntrance": { name: "The Underground", cameraLoc: "public" },
"clubPrivate": { name: "The Dungeon", cameraLoc: "public" },
"clubStrip": { name: "The Velvet Rose", cameraLoc: "public" },
"motelRedLight": { name: "The Stardust", cameraLoc: "public" },
"parlorMassage": { name: "Lotus Spa", cameraLoc: "public" },
"roomsPrivate": { name: "VIP Rooms", cameraLoc: "public" },
"storeAdult": { name: "Eros Boutique", cameraLoc: "public" },
};
/* =====================================================================
LOCATION HIERARCHY & TYPES
=====================================================================
Types:
- district : City region (downTown, maplewood) - 15 min between
- area : Open area/street (streetFifth, beach) - 5 min between
- building : Large Building (mall, hotel) - 5 min between floors/areas
- house : Small Building (FamilyHouse) - 1 min between rooms
- room : Room/place (fhBedroom, storeClothingA) - 1 min inside same building
- subroom : Inner area (fhWardrobe, backroom) - 1 min
===================================================================== */
setup.locations = {
/* ==========================================
DISTRICTS (Root level zones)
========================================== */
"downTown": { parent: null, type: "district" },
"maplewood": { parent: null, type: "district" },
"hillcrest": { parent: null, type: "district" },
"marinaBay": { parent: null, type: "district" },
"oldTown": { parent: null, type: "district" },
"universityDistrict": { parent: null, type: "district" },
"southside": { parent: null, type: "district" },
"suburbs": { parent: null, type: "district" },
"redLightCenter": { parent: null, type: "district" },
/* ==========================================
MAPLEWOOD
========================================== */
// Areas
"mapleChurch": { parent: "maplewood", type: "room" },
"storeCorner": { parent: "maplewood", type: "room" },
"sunsetPark": { parent: "maplewood", type: "area" },
"sunsetWC": { parent: "sunsetPark", type: "subroom" },
// Family House
"FamilyHouse": { parent: "maplewood", type: "house" },
"fhUpperstairs": { parent: "FamilyHouse", type: "room" },
"fhDownstairs": { parent: "FamilyHouse", type: "room" },
"fhBedroom": { parent: "FamilyHouse", type: "room" },
"Upstairs": { parent: "fhBedroom", type: "subroom" },
"fhWardrobe": { parent: "fhBedroom", type: "subroom" },
"fhBrotherRoom": { parent: "FamilyHouse", type: "room" },
"fhParentsRoom": { parent: "FamilyHouse", type: "room" },
"fhParentsBath": { parent: "fhParentsRoom", type: "subroom" },
"fhUpperBath": { parent: "FamilyHouse", type: "room" },
"fhLaundryRoom": { parent: "FamilyHouse", type: "room" },
"fhKitchen": { parent: "FamilyHouse", type: "room" },
"fhLivingroom": { parent: "FamilyHouse", type: "room" },
"fhDownbath": { parent: "FamilyHouse", type: "room" },
"fhBackyard": { parent: "FamilyHouse", type: "room" },
"fhGarage": { parent: "FamilyHouse", type: "room" },
/* ==========================================
DOWNTOWN
========================================== */
// Direct children
"cityHall": { parent: "downTown", type: "building" },
"nightClub": { parent: "downTown", type: "building" },
"restaurantDowntown": { parent: "downTown", type: "room" },
// Mall
"mall": { parent: "downTown", type: "building" },
"floorGround": { parent: "mall", type: "area" },
"floorSecond": { parent: "mall", type: "area" },
"floorThird": { parent: "mall", type: "area" },
// Mall Ground Floor Stores
"storeClothingA": { parent: "floorGround", type: "room" },
"storeCosmetics": { parent: "floorGround", type: "room" },
"storeElectronics": { parent: "floorGround", type: "room" },
"storeShoeA": { parent: "floorGround", type: "room" },
"storeSports": { parent: "floorGround", type: "room" },
"storeSwim": { parent: "floorGround", type: "room" },
"mallBathroom": { parent: "mall", type: "room" },
// Mall Second Floor Stores
"storeBags": { parent: "floorSecond", type: "room" },
"storeClothingB": { parent: "floorSecond", type: "room" },
"storeClothingC": { parent: "floorSecond", type: "room" },
"storeJewelry": { parent: "floorSecond", type: "room" },
"storeLingerieA": { parent: "floorSecond", type: "room" },
"storeLingerieB": { parent: "floorSecond", type: "room" },
"storeShoeB": { parent: "floorSecond", type: "room" },
// Mall Third Floor
"foodCourtMall": { parent: "floorThird", type: "room" },
"salonBeautyMall": { parent: "floorThird", type: "room" },
"theatreMovie": { parent: "floorThird", type: "room" },
// Skyline
"skyline": { parent: "downTown", type: "building" },
"towerA": { parent: "skyline", type: "area" },
"towerB": { parent: "skyline", type: "area" },
"towerC": { parent: "skyline", type: "area" },
// Tower A
"recreationCenter": { parent: "towerA", type: "building" },
"courtBasketball": { parent: "recreationCenter", type: "room" },
"courtVolleyball": { parent: "recreationCenter", type: "room" },
"gym": { parent: "towerA", type: "room" },
"gymMaleLocker": { parent: "gym", type: "room" },
"gymMaleLockerShower": { parent: "gymMaleLocker", type: "subroom" },
"gymFemaleLocker": { parent: "gym", type: "room" },
"gymFemaleLockerWardrobe": { parent: "gymFemaleLocker", type: "subroom" },
// Tower B
"bank": { parent: "towerB", type: "room" },
// Tower C
"hotel": { parent: "towerC", type: "building" },
"jazzClub": { parent: "towerC", type: "room" },
"pethouseSuite": { parent: "towerC", type: "room" },
"restaurantRoof": { parent: "towerC", type: "room" },
"roofTopLounge": { parent: "towerC", type: "room" },
/* ==========================================
HILLCREST
========================================== */
"galleryArt": { parent: "hillcrest", type: "building" },
"golfClub": { parent: "hillcrest", type: "building" },
"golfCourse": { parent: "golfClub", type: "area" },
"golfRestaurant": { parent: "golfClub", type: "room" },
"golfSpa": { parent: "golfClub", type: "room" },
"streetFifth": { parent: "hillcrest", type: "area" },
"barWine": { parent: "streetFifth", type: "room" },
"boutiqueA": { parent: "streetFifth", type: "room" },
"boutiqueB": { parent: "streetFifth", type: "room" },
"boutiqueC": { parent: "streetFifth", type: "room" },
"restaurantLuxury": { parent: "streetFifth", type: "room" },
"salonBeautyFifth": { parent: "streetFifth", type: "room" },
/* ==========================================
MARINA BAY
========================================== */
"marina": { parent: "marinaBay", type: "area" },
"beach": { parent: "marinaBay", type: "area" },
"beachBar": { parent: "beach", type: "room" },
"beachVolleyball": { parent: "beach", type: "area" },
"beachClub": { parent: "marinaBay", type: "building" },
"pier": { parent: "marinaBay", type: "area" },
"pierIcecream": { parent: "pier", type: "room" },
"restaurantBeach": { parent: "marinaBay", type: "room" },
/* ==========================================
OLD TOWN
========================================== */
"civicCenter": { parent: "oldTown", type: "area" },
"townHall": { parent: "civicCenter", type: "building" },
"highSchool": { parent: "civicCenter", type: "building" },
"libraryOldtown": { parent: "civicCenter", type: "building" },
"pharmacy": { parent: "oldTown", type: "room" },
"policeStation": { parent: "civicCenter", type: "building" },
"postOffice": { parent: "civicCenter", type: "room" },
"dinerRubys": { parent: "oldTown", type: "room" },
"dinerRubysBathroom": { parent: "dinerRubys", type: "subroom" },
"dinerRubysKitchen": { parent: "dinerRubys", type: "subroom" },
"dinerRubysCorridor": { parent: "dinerRubys", type: "subroom" },
"dinerRubysManagerOffice": { parent: "dinerRubysCorridor", type: "subroom" },
"dinerRubysDressingRoom": { parent: "dinerRubysCorridor", type: "subroom" },
"dinerRubysStorage": { parent: "dinerRubysCorridor", type: "subroom" },
"shopBarber": { parent: "oldTown", type: "room" },
"shopCoffeeOldtown": { parent: "oldTown", type: "room" },
"storeBook": { parent: "oldTown", type: "room" },
"storeHardware": { parent: "oldTown", type: "room" },
"hospital": { parent: "civicCenter", type: "building" },
/* ==========================================
UNIVERSITY DISTRICT
========================================== */
"campus": { parent: "universityDistrict", type: "area" },
"cafeteriaUni": { parent: "campus", type: "room" },
"lectureHall": { parent: "campus", type: "building" },
"libraryUni": { parent: "campus", type: "building" },
"dormsUni": { parent: "universityDistrict", type: "building" },
"fratHouse": { parent: "universityDistrict", type: "building" },
"sororityHouse": { parent: "universityDistrict", type: "building" },
"studentBar": { parent: "universityDistrict", type: "room" },
/* ==========================================
SOUTHSIDE
========================================== */
"apartmentComplex": { parent: "southside", type: "building" },
"cornerBlock": { parent: "southside", type: "area" },
"laundromat": { parent: "southside", type: "room" },
"oldFactory": { parent: "southside", type: "building" },
"shopPawn": { parent: "southside", type: "room" },
"storeLiquor": { parent: "southside", type: "room" },
/* ==========================================
SUBURBS
========================================== */
"apartmentsSuburbs": { parent: "suburbs", type: "building" },
"houseCrack": { parent: "suburbs", type: "building" },
"motelSuburbs": { parent: "suburbs", type: "building" },
"thePit": { parent: "suburbs", type: "area" },
"gangTerritory": { parent: "suburbs", type: "area" },
"dealerCorner": { parent: "gangTerritory", type: "room" },
"wallGraffiti": { parent: "gangTerritory", type: "area" },
/* ==========================================
RED LIGHT CENTER
========================================== */
"alleyBack": { parent: "redLightCenter", type: "area" },
"basementEntrance": { parent: "redLightCenter", type: "area" },
"barGloryHole": { parent: "basementEntrance", type: "room" },
"barNeon": { parent: "basementEntrance", type: "room" },
"clubStrip": { parent: "basementEntrance", type: "building" },
"clubPrivate": { parent: "redLightCenter", type: "room" },
"roomsPrivate": { parent: "redLightCenter", type: "subroom" },
"motelRedLight": { parent: "redLightCenter", type: "building" },
"parlorMassage": { parent: "redLightCenter", type: "room" },
"storeAdult": { parent: "redLightCenter", type: "room" }
};
/* =====================================================================
TRAVEL TIME CALCULATION
=====================================================================
Type-based travel time:
- room/subroom ↔ same parent : 1 min
- room → parent : 1 min
- subroom ↔ parent : 1 min
- area/building ↔ same parent: 5 min
- district ↔ district : 15 min
===================================================================== */
// Get the root district for a location
setup.getDistrict = function(locationId) {
let current = locationId;
let data = setup.locations[current];
while (data && data.parent) {
current = data.parent;
data = setup.locations[current];
}
return current;
};
// Get the building containing a location (if any)
setup.getBuilding = function(locationId) {
let current = locationId;
let data = setup.locations[current];
while (data) {
if (data.type === "building" || data.type === "house") {
return current;
}
if (!data.parent) break;
current = data.parent;
data = setup.locations[current];
}
return null;
};
// Calculate travel time between locations
setup.getTravelTime = function(fromLocation, toLocation) {
const fromData = setup.locations[fromLocation] || {};
const toData = setup.locations[toLocation] || {};
const fromType = fromData.type || "area";
const toType = toData.type || "area";
const fromParent = fromData.parent;
const toParent = toData.parent;
// Same location = 0
if (fromLocation === toLocation) return 0;
// Special Case: "Upstairs" navCard in fhBedroom is a logic gate.
// Cost is 0 initially. Time is added manually upon success.
if (toLocation === "Upstairs") return 0;
// Direct parent-child: room/subroom ↔ parent = 1 min
if (fromParent === toLocation || toParent === fromLocation) {
if (fromType === "room" || fromType === "subroom" ||
toType === "room" || toType === "subroom") {
return 1;
}
}
// Same parent: rooms in same building = 1 min
if (fromParent && fromParent === toParent) {
if ((fromType === "room" || fromType === "subroom") &&
(toType === "room" || toType === "subroom")) {
return 1;
}
}
// Same building: rooms within = 1 min
const fromBuilding = setup.getBuilding(fromLocation);
const toBuilding = setup.getBuilding(toLocation);
if (fromBuilding && fromBuilding === toBuilding) {
const buildingData = setup.locations[fromBuilding];
// Special Case: House is small, always 1 min
if (buildingData && buildingData.type === "house") {
return 1;
}
if ((fromType === "room" || fromType === "subroom") &&
(toType === "room" || toType === "subroom")) {
return 1;
}
return 5; // Building internal but area-level
}
// Same district = 5 min
const fromDistrict = setup.getDistrict(fromLocation);
const toDistrict = setup.getDistrict(toLocation);
if (fromDistrict === toDistrict) {
return 5;
}
// Different district = 15 min
return 15;
};
<</script>>/* ==========================================
PEOPLE SYSTEM - Base Containers
This file creates empty containers that are
populated by CharacterInit_[Name][INIT] files
========================================== */
/* Character definitions - populated by CharacterInit files.
Optional relationship: put in setup.characterDefs[id].relationship or in $characters[id].relationship (e.g. family).
Schema: { status: "single"|"dating"|"serious"|"married"|"divorced", partnerLabel?: "wife"|"girlfriend"|"boyfriend"|"ex-wife" etc., hasChildren?: true }.
Helpers: setup.getCharacterRelationship(id), setup.hasRelationshipStatus(id, status), setup.relationshipLabel(id). */
<<set $characters = {}>>
/* Static NPC data (name, avatar, etc.) – not saved. Core (father, mother, brother, player) stay only in $characters. */
<<set setup.characterDefs = {}>>
/* Family: always in phone contact list (have their numbers from the start). Others added via <<phoneUnlockContact charId>> when player swaps numbers. */
<<set setup.phoneContactsFamily = setup.phoneContactsFamily || ["mother", "father", "brother"]>>
/* Locations (oldTown, downTown, maplewood...) are read from setup.locations (parent === null).
Places per location: defined here. Only places with at least one known member appear; places are expandable. */
<<set setup.relationPlaces = [
{ locationId: "maplewood", placeId: "FamilyHouse", name: "Family House", members: ["father", "mother", "brother"] },
{ locationId: "maplewood", placeId: "storeCorner", name: "Quick Mart", members: ["shopClerkMarcus"] },
{ locationId: "maplewood", placeId: "sunsetPark", name: "Sunset Park", members: ["parkRunnerLily"] },
{ locationId: "maplewood", placeId: "harrisons", name: "Harrisons", members: ["neighborMia"] },
{ locationId: "oldTown", placeId: "dinerRubys", name: "Ruby's Diner", members: ["dinerClerk", "dinerDishwasher", "dinerChef", "dinerManager", "dinerWaitress1", "dinerWaitress2", "dinerWaitress3"] }
]>>
/* Dynamic relation group labels (runtime-generated members are injected by JS). */
<<set setup.relationDynamicGroups = setup.relationDynamicGroups || {
fotogram: {
groupName: "Fotogram"
}
}>>
/* Schedule and action containers */
<<set setup.schedules = {}>>
<<set setup.characterActions = {}>><<set $phoneGallery = {
photos: {
normal: [],
cute: [],
hot: [],
spicy: [],
received: []
},
videos: {
normal: [],
cute: [],
hot: [],
spicy: [],
received: []
},
fotogram: {
received: [],
sended: []
}
}>>
/*
Phone Gallery Structure:
- Each item: { id, path, flags, timestamp, quality, from }
- id: unique identifier
- path: image/video path (empty until variant from pool; pool sets it in Step 6)
- flags: string[] e.g. ["casual","clothed"] or ["nude","public","risky","special{public_sunsetPark}"] (location context in flags)
- timestamp: { day, month, year, hour, minute }
- quality: 1-100 score from stats (Beauty + Looks + Confidence + Exhibitionism)
- from: 'player' or charId (for received)
*//* =============================================================================
PHONE MEDIA POOLS – Camera & Fotogram
=============================================================================
Camera picks paths from these pools by category + location bucket (global/safe/public).
Gallery item: { id, path, flags, timestamp, quality, from }.
Same path + higher quality → replaces old item (phone-camera.js).
Fotogram: flags determine post eligibility (Instagram-style; topless/nude not allowed).
STRUCTURE:
setup.phoneMediaPools = {
photos: {
normal: { global: [...], safe: [...], public: [...] },
cute: { global: [...], safe: [...], public: [...] },
hot: { global: [...], safe: [...], public: [...] },
spicy: { global: [...], safe: [...], public: [...] }
},
videos: { same }
}
ENTRY: { path, tags?, flags?, conditions? }
- path: asset path (e.g. "assets/content/phone/gallery/photos/normal/normalSelfie1.webp")
- tags: ["locationId"] – locationId = last segment of $location. When shooting there,
those images preferred; if none match, any in slot used. E.g. ["sunsetPark"].
- flags: ["risky"] | ["teasing"] | ["bikini"] | etc. Applied to gallery item.
Fotogram: risky/teasing/suggestive/bikini = postable. topless/nude/explicit = not postable.
- conditions: Stat/outfit/location checks. Entry used only if ALL pass.
{ fitness: 50 } – min $fitness
{ corruption: 3 } – min $corruption
{ exhibitionism: 40 } – min $exhibitionism
{ confidence: 30 } – min $confidence
{ beauty: 50 } – min $beauty
{ outfitStyle: "sporty", outfitMinItems: 1 } – wearing sporty (1+ items)
{ location: "fhBathroom" } or { location: ["fhBathroom","fhBedroom"] }
{ requiredFlags: "phoneParkUnlocked" } or { forbiddenFlags: ["embarrassedToday"] }
Example: Fitness 50 + sporty outfit + fhBathroom → photoNameX
FULL EXAMPLE:
photos: {
normal: {
global: [
{ path: "assets/content/phone/gallery/photos/normal/normalSelfie1.webp", tags: [], flags: ["casual"] }
],
safe: [
{ path: "assets/content/phone/gallery/photos/normal/normalSelfie1.webp", tags: [], flags: ["casual"] },
{ path: "assets/content/phone/camera/normal_safe_2.webp", tags: ["fhBedroom"], flags: ["casual","clothed"] }
],
public: [
{ path: "assets/content/phone/camera/normal_public_1.webp", tags: ["sunsetPark"], flags: ["casual"] }
]
},
spicy: {
global: [
{ path: "assets/.../spicy_generic_1.webp", tags: [], flags: ["teasing","risky"] }
],
safe: [
{ path: "assets/.../spicy_tease_1.webp", tags: [], flags: ["teasing","risky"] }, // Fotogram OK
{ path: "assets/.../spicy_topless_1.webp", tags: [], flags: ["topless"] } // Fotogram NOT OK
],
public: [
{ path: "assets/.../spicy_bikini_1.webp", tags: ["sunsetPark"], flags: ["bikini","risky"] } // Fotogram OK
]
}
}
============================================================================= */
<<set setup.phoneMediaPools = {
photos: {
normal: {
global: [
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie1.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie2.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie3.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie4.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie5.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie6.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie7.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie8.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie9.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie10.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie11.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie12.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie13.webp" },
{ path: "assets/content/phone/gallery/photos/normal/global/normalSelfie14.webp" }
],
safe: [],
public: [],
},
cute: {
global: [
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie1.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie2.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie3.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie4.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie5.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie6.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie7.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie8.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie9.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie10.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie11.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie12.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie13.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie15.webp" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie15.jpg" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie16.jpg" },
{ path: "assets/content/phone/gallery/photos/cute/global/cuteSelfie17.jpg" }
],
safe: [],
public: []
},
hot: {
global: [
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie1.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie2.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie3.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie3.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie4.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie5.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie6.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie7.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie8.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie9.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie10.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie11.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie12.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie13.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie14.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie15.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie16.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie17.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie18.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie19.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie20.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie21.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie22.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie23.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie24.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie25.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie26.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie27.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie28.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie29.webp" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie30.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie31.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie32.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie33.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie34.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie35.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie36.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie37.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie38.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie39.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie40.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie41.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie42.jpg" },
{ path: "assets/content/phone/gallery/photos/hot/global/hotSelfie43.jpg" }
],
safe: [],
public: []
},
spicy: {
global: [],
safe: [],
public: []
}
},
videos: {
normal: {
global: [
{ path: "assets/content/phone/gallery/videos/normal/global/normalVideo1.mp4" },
{ path: "assets/content/phone/gallery/videos/normal/global/normalVideo2.mp4" },
{ path: "assets/content/phone/gallery/videos/normal/global/normalVideo3.mp4" }
],
safe: [],
public: []
},
cute: { global: [
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo1.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo2.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo2_out.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo3.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo4.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo5.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo6.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo7.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo8.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo9.mp4" },
{ path: "assets/content/phone/gallery/videos/cute/global/cuteVideo10.mp4" }
], safe: [], public: [] },
hot: { global: [
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo1.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo2.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo3.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo4.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo5.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo6.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo7.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo8.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo9.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo10.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo11.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo12.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo13.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo14.mp4" },
{ path: "assets/content/phone/gallery/videos/hot/global/hotVideo15.mp4" }
], safe: [], public: [] },
spicy: { global: [], safe: [], public: [] }
}
}>>/* Centralized phone photo pools:
- setup.phonePhotos for topic imagePool selections
- setup.fotogramDMPhotoPool for Fotogram DM attachments (anon sends)
- setup.fdmPlayerPhotos for Fotogram DM player sends (interactive DM – below)
PLAYER DM ASSETS – menu order: Normal | Sexy | Nude | Ass | Boobs | Pussy | no
Actual paths below (e.g. photoPlayerSexy1.webp). Legacy p_<style>_01 names are resolved via fallback in phone-fotogram-dm.js. */
<<set setup.phonePhotos = {
mother: [
"assets/content/phone/camera/normal_safe_1.webp",
],
father: [
"assets/content/phone/camera/normal_safe_1.webp",
],
brother: [
"assets/content/phone/camera/normal_safe_1.webp",
],
player: [
"assets/content/phone/camera/normal_safe_1.webp",
]
}>>
/* Backward compatibility */
<<set setup.phoneSelfiePaths = setup.phonePhotos>>
<<run setup.fotogramDMPhotoPool = {
spicy: {
male: {
white: ["assets/content/phone/dm/male/spicy/white/photoSpicyWhite1.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite2.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite3.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite4.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite5.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite6.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite7.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite8.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite9.webp","assets/content/phone/dm/male/spicy/white/photoSpicyWhite10.webp"],
black: ["assets/content/phone/dm/male/spicy/black/photoSpicyBlack1.webp","assets/content/phone/dm/male/spicy/black/photoSpicyBlack2.webp","assets/content/phone/dm/male/spicy/black/photoSpicyBlack3.webp","assets/content/phone/dm/male/spicy/black/photoSpicyBlack4.webp","assets/content/phone/dm/male/spicy/black/photoSpicyBlack5.webp","assets/content/phone/dm/male/spicy/black/photoSpicyBlack6.webp","assets/content/phone/dm/male/spicy/black/photoSpicyBlack7.webp","assets/content/phone/dm/male/spicy/black/photoSpicyBlack8.webp","assets/content/phone/dm/male/spicy/black/photoSpicyBlack9.webp"]
},
female: {
white: ["assets/content/phone/dm/female/spicy/photoFemaleSpicy1.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy2.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy3.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy4.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy5.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy6.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy7.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy9.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy10.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy11.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy12.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy13.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy14.webp",
"assets/content/phone/dm/female/spicy/photoFemaleSpicy15.webp",
{ path:"assets/content/phone/dm/female/spicy/videoFemaleSpicy1.mp4", kind:"video" },
{ path:"assets/content/phone/dm/female/spicy/videoFemaleSpicy2.mp4", kind:"video" }
]
}
},
sexting: {
female: {
white: ["assets/content/phone/dm/female/sexting/photoFemaleSexting1.webp",
"assets/content/phone/dm/female/sexting/photoFemaleSexting2.webp",
"assets/content/phone/dm/female/sexting/photoFemaleSexting3.webp",
"assets/content/phone/dm/female/sexting/photoFemaleSexting4.webp",
"assets/content/phone/dm/female/sexting/photoFemaleSexting5.webp",
"assets/content/phone/dm/female/sexting/photoFemaleSexting6.webp",
{ path: "assets/content/phone/dm/female/sexting/videoFemaleSexting1.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/sexting/videoFemaleSexting2.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/sexting/videoFemaleSexting3.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/sexting/videoFemaleSexting4.mp4", kind: "video" }
]
}
},
cock: {
white: ["assets/content/phone/dm/male/cock/white/photoCockWhite1.webp","assets/content/phone/dm/male/cock/white/photoCockWhite2.webp","assets/content/phone/dm/male/cock/white/photoCockWhite3.webp","assets/content/phone/dm/male/cock/white/photoCockWhite4.webp","assets/content/phone/dm/male/cock/white/photoCockWhite5.webp","assets/content/phone/dm/male/cock/white/photoCockWhite6.webp","assets/content/phone/dm/male/cock/white/photoCockWhite7.webp","assets/content/phone/dm/male/cock/white/photoCockWhite8.webp","assets/content/phone/dm/male/cock/white/photoCockWhite9.jpeg","assets/content/phone/dm/male/cock/white/photoCockWhite10.jpg",{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite1.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite2.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite3.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite4.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite5.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite6.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite7.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite8.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite9.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite10.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/white/videoCockWhite11.mp4", kind: "video" }],
black: ["assets/content/phone/dm/male/cock/black/photoCockBlack1.webp","assets/content/phone/dm/male/cock/black/photoCockBlack2.webp","assets/content/phone/dm/male/cock/black/photoCockBlack3.webp","assets/content/phone/dm/male/cock/black/photoCockBlack4.webp","assets/content/phone/dm/male/cock/black/photoCockBlack5.webp","assets/content/phone/dm/male/cock/black/photoCockBlack6.webp","assets/content/phone/dm/male/cock/black/photoCockBlack7.webp","assets/content/phone/dm/male/cock/black/photoCockBlack8.webp","assets/content/phone/dm/male/cock/black/photoCockBlack9.webp","assets/content/phone/dm/male/cock/black/photoCockBlack10.webp",{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack1.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack2.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack3.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack4.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack5.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack6.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack7.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack8.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack9.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack10.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cock/black/videoCockBlack11.mp4", kind: "video" }]
},
cum: {
white: [{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite1.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite2.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite3.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite4.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite5.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite6.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite7.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite8.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite9.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite10.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/white/videoCumWhite11.mp4", kind: "video" }],
black: [{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack1.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack2.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack3.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack4.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack5.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack6.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack7.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack8.mp4", kind: "video" },{ path: "assets/content/phone/dm/male/cum/black/videoCumBlack9.mp4", kind: "video" }]
},
female_masturbation: {
white: [{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation1.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation2.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation3.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation4.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation5.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation6.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation7.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation8.mp4", kind: "video" },
{ path: "assets/content/phone/dm/female/masturbation/FemaleMasturbation9.mp4", kind: "video" }
]
}
}>>
/* Fotogram interactive DM – player (female protagonist) photo/video pool. Mix webp + video per category. */
<<run setup.fdmPlayerPhotos = {
normal: [
"assets/content/phone/dm/player/normal/photoPlayerNormal1.webp",
"assets/content/phone/dm/player/normal/photoPlayerNormal2.webp",
"assets/content/phone/dm/player/normal/photoPlayerNormal3.webp",
"assets/content/phone/dm/player/normal/photoPlayerNormal4.webp",
"assets/content/phone/dm/player/normal/photoPlayerNormal5.webp",
"assets/content/phone/dm/player/normal/photoPlayerNormal6.webp",
"assets/content/phone/dm/player/normal/photoPlayerNormal7.webp",
"assets/content/phone/dm/player/normal/photoPlayerNormal8.webp"
],
sexy: [
"assets/content/phone/dm/player/sexy/photoPlayerSexy1.webp",
"assets/content/phone/dm/player/sexy/photoPlayerSexy2.webp",
"assets/content/phone/dm/player/sexy/photoPlayerSexy3.webp",
"assets/content/phone/dm/player/sexy/photoPlayerSexy4.webp",
"assets/content/phone/dm/player/sexy/photoPlayerSexy5.webp",
"assets/content/phone/dm/player/sexy/photoPlayerSexy6.webp",
"assets/content/phone/dm/player/sexy/photoPlayerSexy7.webp",
"assets/content/phone/dm/player/sexy/photoPlayerSexy8.webp",
"assets/content/phone/dm/player/sexy/photoPlayerSexy9.webp",
],
nude: [
"assets/content/phone/dm/player/nude/photoPlayerNude1.webp",
"assets/content/phone/dm/player/nude/photoPlayerNude2.webp",
"assets/content/phone/dm/player/nude/photoPlayerNude3.webp",
"assets/content/phone/dm/player/nude/photoPlayerNude4.webp",
"assets/content/phone/dm/player/nude/photoPlayerNude5.webp",
"assets/content/phone/dm/player/nude/photoPlayerNude6.webp",
"assets/content/phone/dm/player/nude/photoPlayerNude7.webp",
{ path:"assets/content/phone/dm/player/nude/videoPlayerNude1.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/nude/videoPlayerNude2.mp4", kind:"video" }
],
ass: [
"assets/content/phone/dm/player/ass/photoPlayerAss1.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss2.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss3.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss4.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss5.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss6.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss7.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss8.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss9.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss10.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss11.webp",
"assets/content/phone/dm/player/ass/photoPlayerAss12.webp",
{ path:"assets/content/phone/dm/player/ass/videoPlayerAss1.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/ass/videoPlayerAss2.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/ass/videoPlayerAss3.mp4", kind:"video" }
],
boobs: [
"assets/content/phone/dm/player/boobs/photoPlayerBoobs1.webp",
"assets/content/phone/dm/player/boobs/photoPlayerBoobs2.webp",
"assets/content/phone/dm/player/boobs/photoPlayerBoobs3.webp",
"assets/content/phone/dm/player/boobs/photoPlayerBoobs4.webp",
"assets/content/phone/dm/player/boobs/photoPlayerBoobs5.webp",
{ path:"assets/content/phone/dm/player/boobs/videoPlayerBoobs1.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/boobs/videoPlayerBoobs2.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/boobs/videoPlayerBoobs3.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/boobs/videoPlayerBoobs4.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/boobs/videoPlayerBoobs5.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/boobs/videoPlayerBoobs6.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/boobs/videoPlayerBoobs7.mp4", kind:"video" }
],
pussy: [
"assets/content/phone/dm/player/pussy/photoPlayerPussy1.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy2.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy3.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy4.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy5.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy6.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy7.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy8.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy9.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy10.webp",
"assets/content/phone/dm/player/pussy/photoPlayerPussy11.webp",
{ path:"assets/content/phone/dm/player/pussy/videoPlayerPussy1.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/pussy/videoPlayerPussy2.mp4", kind:"video" },
{ path:"assets/content/phone/dm/player/pussy/videoPlayerPussy3.mp4", kind:"video" },
]
}>>/* Brother phone topics – 2–4 variations per topic, slang/profanity ok */
<<if !setup.phoneMessageTopics>><<set setup.phoneMessageTopics = {}>><</if>>
<<set setup.phoneMessageTopics.brother = [
/* === FRIENDSHIP === */
{
id: "sup",
version: 1,
label: "What's up?",
category: "friendship",
tier: 0,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "yo what are you doing" },
{ from: "character", text: "nothing much just gaming. why whats up" },
{ from: "player", text: "bored. wanna do something later?" },
{ from: "character", text: "yeah maybe. hit me up" }
],
[
{ from: "player", text: "hey what's good" },
{ from: "character", text: "chillin. whats up with you" },
{ from: "player", text: "same. just checking in" },
{ from: "character", text: "cool. text me if you need anything" }
],
[
{ from: "player", text: "yo what are you doing" },
{ from: "character", text: "nothing much just gaming. why whats up" },
{ from: "player", text: "bored. wanna do something later? just checking in" },
{ from: "character", text: "yeah maybe. hit me up. homework lol. so bored. you?" },
{ from: "player", text: "same. text you later." }
],
[
{ from: "player", text: "hey what's good" },
{ from: "character", text: "chillin. whats up with you. just watching some stuff. bored tbh" },
{ from: "player", text: "same. not much. you?" },
{ from: "character", text: "cool. text me if you need anything. we could hang" },
{ from: "player", text: "ok. bye." }
],
[
{ from: "player", text: "yo what are you doing" },
{ from: "character", text: "nothing much. wanna hang later? or game?" },
{ type: "choice", options: [
{ label: "Hang", playerText: "yeah lets hang. when?", then: [ { from: "character", text: "tonight? hit me up when youre free. cool" } ] },
{ label: "Game", playerText: "lets game. what are we playing?", then: [ { from: "character", text: "idk you pick. text me when. yeah im down" } ] }
]}
],
[
{ from: "player", text: "hey what's good" },
{ from: "character", text: "chillin. you wanna do something? bored as hell" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. grab food?", then: [ { from: "character", text: "down. when? text me. cool" } ] },
{ label: "Later", playerText: "maybe later. just checking in", then: [ { from: "character", text: "ok. hit me up. love you" } ] }
]}
]
]
},
{
id: "bored",
version: 1,
label: "Bored?",
category: "friendship",
tier: 0,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "you bored?" },
{ from: "character", text: "yeah kinda. wanna do something?" },
{ from: "player", text: "maybe. got any ideas?" },
{ from: "character", text: "idk. we could game or some shit" }
],
[
{ from: "player", text: "bored as hell" },
{ from: "character", text: "always lol. got any ideas?" },
{ from: "player", text: "not really. you?" },
{ from: "character", text: "meh. same old. we could hang later" }
],
[
{ from: "player", text: "you bored?" },
{ from: "character", text: "yeah kinda. wanna do something?" },
{ from: "player", text: "maybe. got any ideas? im dying over here" },
{ from: "character", text: "idk. we could game or some shit. or just chill" },
{ from: "player", text: "cool. hit me up" }
],
[
{ from: "player", text: "bored as hell" },
{ from: "character", text: "always lol. got any ideas?" },
{ from: "player", text: "not really. you? we could do something" },
{ from: "character", text: "meh. same old. we could hang later. yeah. got plans?" },
{ from: "player", text: "nah. later then." }
],
[
{ from: "player", text: "you bored?" },
{ from: "character", text: "yeah. wanna game or go out? your call" },
{ type: "choice", options: [
{ label: "Game", playerText: "game. what we playing?", then: [ { from: "character", text: "idk. you pick. text me when. yeah" } ] },
{ label: "Go out", playerText: "go out. food?", then: [ { from: "character", text: "down. when? hit me up. cool" } ] }
]}
],
[
{ from: "player", text: "bored as hell" },
{ from: "character", text: "same. you got plans? we could do something" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. tonight?", then: [ { from: "character", text: "cool. text me. will do" } ] },
{ label: "Nah", playerText: "nah. just complaining lol", then: [ { from: "character", text: "lol same. hit me up if you wanna hang. later" } ] }
]}
]
]
},
{
id: "hangLater",
version: 1,
label: "Hang out later?",
category: "friendship",
tier: 20,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "wanna hang out later?" },
{ from: "character", text: "yeah sure. what did you have in mind?" },
{ from: "player", text: "idk. just something" },
{ from: "character", text: "ok cool. text me when youre free" }
],
[
{ from: "player", text: "down to hang later?" },
{ from: "character", text: "yeah im down. lets do something fun" },
{ from: "player", text: "sure. hit me up" },
{ from: "character", text: "will do" }
],
[
{ from: "player", text: "wanna hang out later?" },
{ from: "character", text: "yeah sure. what did you have in mind?" },
{ from: "player", text: "idk. just something. get out of the house" },
{ from: "character", text: "ok cool. text me when youre free. yeah im down. lets do something fun" },
{ from: "player", text: "sure. hit me up" }
],
[
{ from: "player", text: "down to hang later?" },
{ from: "character", text: "yeah im down. lets do something fun" },
{ from: "player", text: "sure. hit me up. what do you wanna do?" },
{ from: "character", text: "will do. maybe grab food or some shit. idk" },
{ from: "player", text: "cool. later." }
],
[
{ from: "player", text: "wanna hang out later?" },
{ from: "character", text: "yeah. you wanna game or do something else?" },
{ type: "choice", options: [
{ label: "Game", playerText: "game. then maybe food?", then: [ { from: "character", text: "down. text me when. will do" } ] },
{ label: "Just hang", playerText: "just hang. your place or mine?", then: [ { from: "character", text: "idk. either. hit me up. cool" } ] }
]}
],
[
{ from: "player", text: "down to hang later?" },
{ from: "character", text: "yeah im down. what do you wanna do? game? food?" },
{ type: "choice", options: [
{ label: "Game", playerText: "game first. then we see", then: [ { from: "character", text: "ok. text me when. yeah" } ] },
{ label: "Food", playerText: "grab food. you drive?", then: [ { from: "character", text: "sure. when? hit me up. later" } ] }
]}
]
]
},
{
id: "gaming",
version: 1,
label: "Game together?",
category: "friendship",
tier: 20,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "wanna play something later?" },
{ from: "character", text: "yeah for sure. what game?" },
{ from: "player", text: "whatever. you pick" },
{ from: "character", text: "ok cool. text me when" }
],
[
{ from: "player", text: "game later?" },
{ from: "character", text: "yeah im down. been wanting to play anyway" },
{ from: "player", text: "same" },
{ from: "character", text: "yeah. what do you wanna play?" }
],
[
{ from: "player", text: "wanna play something later?" },
{ from: "character", text: "yeah for sure. what game?" },
{ from: "player", text: "whatever. you pick. im easy" },
{ from: "character", text: "ok cool. text me when. been wanting to play anyway" },
{ from: "player", text: "will do. bye" }
],
[
{ from: "player", text: "game later?" },
{ from: "character", text: "yeah im down. been wanting to play anyway" },
{ from: "player", text: "same. what do you wanna play?" },
{ from: "character", text: "yeah. what do you wanna play? idk surprise me" },
{ from: "player", text: "ok. text you" }
],
[
{ from: "player", text: "wanna play something later?" },
{ from: "character", text: "yeah. what game? or you wanna pick?" },
{ type: "choice", options: [
{ label: "You pick", playerText: "you pick. im easy", then: [ { from: "character", text: "ok. text me when. cool. will do" } ] },
{ label: "Co-op", playerText: "something co-op. got anything?", then: [ { from: "character", text: "yeah. we can figure it out. hit me up. yeah" } ] }
]}
],
[
{ from: "player", text: "game later?" },
{ from: "character", text: "yeah. when? and what we playing?" },
{ type: "choice", options: [
{ label: "Tonight", playerText: "tonight. you pick the game", then: [ { from: "character", text: "down. text me. ok cool" } ] },
{ label: "Tomorrow", playerText: "tomorrow maybe. busy tonight", then: [ { from: "character", text: "ok. hit me up. later" } ] }
]}
]
]
},
{
id: "realTalk",
version: 1,
label: "Real talk",
category: "friendship",
tier: 40,
timeAdvance: 3,
statGain: { friendship: 3 },
variations: [
[
{ from: "player", text: "can we talk about something serious?" },
{ from: "character", text: "sure whats up? everything ok?" },
{ from: "player", text: "yeah. just need to get some stuff off my chest" },
{ from: "character", text: "yeah of course. im listening" }
],
[
{ from: "player", text: "i need to talk. like for real" },
{ from: "character", text: "yeah totally. what's going on?" },
{ from: "player", text: "not over text. later?" },
{ from: "character", text: "yeah. im here. talk to me whenever" }
],
[
{ from: "player", text: "can we talk about something serious?" },
{ from: "character", text: "sure whats up? everything ok?" },
{ from: "player", text: "yeah. just need to get some stuff off my chest. not over text though" },
{ from: "character", text: "yeah of course. im listening. whenever youre ready. face to face" },
{ from: "player", text: "thanks. later" }
],
[
{ from: "player", text: "i need to talk. like for real" },
{ from: "character", text: "yeah totally. what's going on?" },
{ from: "player", text: "not over text. later? its important" },
{ from: "character", text: "yeah. im here. talk to me whenever. sounds serious" },
{ from: "player", text: "it is. thanks. bye" }
]
]
},
{
id: "futurePlans",
version: 1,
label: "Future plans",
category: "friendship",
tier: 60,
timeAdvance: 3,
statGain: { friendship: 4 },
variations: [
[
{ from: "player", text: "what do you think youll do after school?" },
{ from: "character", text: "idk honestly. been thinking about it a lot. you?" },
{ from: "player", text: "same. we should talk about it sometime" },
{ from: "character", text: "yeah. no clue. still figuring it out" }
],
[
{ from: "player", text: "any plans after graduation?" },
{ from: "character", text: "not sure yet. maybe college? what about you?" },
{ from: "player", text: "figuring it out" },
{ from: "character", text: "same. we got time" }
],
[
{ from: "player", text: "what do you think youll do after school?" },
{ from: "character", text: "idk honestly. been thinking about it a lot. you?" },
{ from: "player", text: "same. we should talk about it sometime. no rush" },
{ from: "character", text: "yeah. no clue. still figuring it out. we got time" },
{ from: "player", text: "cool. later" }
],
[
{ from: "player", text: "any plans after graduation?" },
{ from: "character", text: "not sure yet. maybe college? what about you?" },
{ from: "player", text: "figuring it out. we should talk" },
{ from: "character", text: "same. we got time. no clue. we should talk about it sometime" },
{ from: "player", text: "yeah. bye" }
],
[
{ from: "player", text: "what do you think youll do after school?" },
{ from: "character", text: "idk. you wanna talk about it? we could get coffee or some shit" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. this weekend?", then: [ { from: "character", text: "down. we can figure it out. same. we got time" } ] },
{ label: "Later", playerText: "maybe later. just thinking out loud", then: [ { from: "character", text: "ok. hit me up. we got time. later" } ] }
]}
],
[
{ from: "player", text: "any plans after graduation?" },
{ from: "character", text: "not sure. you? we should talk about it sometime" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. lets talk. when?", then: [ { from: "character", text: "this week? idk. we got time. cool" } ] },
{ label: "Same", playerText: "same. no clue. we'll figure it out", then: [ { from: "character", text: "yeah. no rush. later" } ] }
]}
]
]
},
{
id: "seriousStuff",
version: 1,
label: "Serious stuff",
category: "friendship",
tier: 60,
timeAdvance: 3,
statGain: { friendship: 4 },
variations: [
[
{ from: "player", text: "i need to talk to you about something important" },
{ from: "character", text: "ok yeah. im here. whats up?" },
{ from: "player", text: "cant do it over text. face to face?" },
{ from: "character", text: "yeah of course. whenever youre ready" }
],
[
{ from: "player", text: "something important. need to tell you" },
{ from: "character", text: "sounds serious. talk to me. whats going on?" },
{ from: "player", text: "later. promise" },
{ from: "character", text: "im listening. go ahead when you're ready" }
],
[
{ from: "player", text: "i need to talk to you about something important" },
{ from: "character", text: "ok yeah. im here. whats up?" },
{ from: "player", text: "cant do it over text. face to face? its heavy" },
{ from: "character", text: "yeah of course. whenever youre ready. sounds serious" },
{ from: "player", text: "thanks. later" }
],
[
{ from: "player", text: "something important. need to tell you" },
{ from: "character", text: "sounds serious. talk to me. whats going on?" },
{ from: "player", text: "later. promise. not over text" },
{ from: "character", text: "im listening. go ahead when you're ready. yeah. im here" },
{ from: "player", text: "ok. bye" }
],
[
{ from: "player", text: "i need to talk to you about something important" },
{ from: "character", text: "ok. you wanna do it now or face to face? your call" },
{ type: "choice", options: [
{ label: "Face to face", playerText: "face to face. can we?", then: [ { from: "character", text: "yeah. whenever. im here. you know that. love you" } ] },
{ label: "Later", playerText: "later. i'll find you. promise", then: [ { from: "character", text: "ok. im listening. whenever. go ahead when youre ready" } ] }
]}
],
[
{ from: "player", text: "something important. need to tell you" },
{ from: "character", text: "sounds serious. you want to meet up so we can talk properly?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yes. this week?", then: [ { from: "character", text: "name the day. im here. love you. for real" } ] },
{ label: "Not yet", playerText: "not yet. soon. thanks for being there", then: [ { from: "character", text: "always. im here. whenever. love you" } ] }
]}
]
]
},
/* === LOVE === */
{
id: "goodBro",
version: 1,
label: "Good brother",
category: "love",
tier: 20,
timeAdvance: 3,
statGain: { love: 2, friendship: 1 },
variations: [
[
{ from: "player", text: "youre a good brother you know" },
{ from: "character", text: "aw thanks. you too honestly" },
{ from: "player", text: "mean it" },
{ from: "character", text: "haha thanks. so are you. love you" }
],
[
{ from: "player", text: "best brother ever ngl" },
{ from: "character", text: "thats actually really nice. thanks for saying that" },
{ from: "player", text: "just facts" },
{ from: "character", text: "yo thanks. youre pretty great yourself" }
],
[
{ from: "player", text: "youre a good brother you know" },
{ from: "character", text: "aw thanks. you too honestly" },
{ from: "player", text: "mean it. you're the best" },
{ from: "character", text: "haha thanks. so are you. love you. thats actually really nice" },
{ from: "player", text: "love you too. bye" }
],
[
{ from: "player", text: "best brother ever ngl" },
{ from: "character", text: "thats actually really nice. thanks for saying that" },
{ from: "player", text: "just facts. you're awesome" },
{ from: "character", text: "yo thanks. youre pretty great yourself. means a lot" },
{ from: "player", text: "good. bye" }
],
[
{ from: "player", text: "youre a good brother you know" },
{ from: "character", text: "aw thanks. you wanna hang soon? just us?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. when?", then: [ { from: "character", text: "this weekend? game or food. your call. love you" } ] },
{ label: "Soon", playerText: "soon. just wanted to say it", then: [ { from: "character", text: "means a lot. love you too. hit me up" } ] }
]}
],
[
{ from: "player", text: "best brother ever ngl" },
{ from: "character", text: "thats really nice. wanna do something together? game night?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "down. when?", then: [ { from: "character", text: "friday? cool. love you. will do" } ] },
{ label: "Maybe", playerText: "maybe. i'll let you know", then: [ { from: "character", text: "ok. love you. thanks for saying that" } ] }
]}
]
]
},
{
id: "flirtBro",
version: 1,
label: "Flirt",
category: "love",
tier: 40,
requiredStats: { friendship: 20 },
timeAdvance: 3,
statGain: { love: 3, friendship: 1 },
variations: [
[
{ from: "player", text: "youre actually really cute you know" },
{ from: "character", text: "lol what? thanks i guess haha. youre pretty hot yourself" },
{ from: "player", text: "i mean it" },
{ from: "character", text: "wait really? lol thanks. you look amazing honestly" }
],
[
{ from: "player", text: "you look good. like really good" },
{ from: "character", text: "um thanks? youre not so bad yourself honestly" },
{ from: "player", text: "not so bad? try gorgeous" },
{ from: "character", text: "haha ok where is this coming from? but thanks. youre gorgeous btw" }
],
[
{ from: "player", text: "youre actually really cute you know" },
{ from: "character", text: "lol what? thanks i guess haha. youre pretty hot yourself" },
{ from: "player", text: "i mean it. seriously. you're gorgeous" },
{ from: "character", text: "wait really? lol thanks. you look amazing honestly. damn okay" },
{ from: "player", text: "appreciate it. bye" }
],
[
{ from: "player", text: "you look good. like really good" },
{ from: "character", text: "um thanks? youre not so bad yourself honestly" },
{ from: "player", text: "not so bad? try gorgeous. i mean it" },
{ from: "character", text: "haha ok where is this coming from? but thanks. youre gorgeous btw. making me blush" },
{ from: "player", text: "good. bye" }
],
[
{ from: "player", text: "youre actually really cute you know" },
{ from: "character", text: "lol thanks. you wanna send me a pic? you're hot. i'd send one back 😏" },
{ type: "choice", options: [
{ label: "Yes", playerText: "maybe. you first?", then: [ { from: "character", text: "deal. check your phone. you're gorgeous btw 🔥" } ] },
{ label: "Flirty no", playerText: "not now. but you're really hot. just saying", then: [ { from: "character", text: "haha ok. thanks. you too. hit me up if you change your mind 😏" } ] }
]}
],
[
{ from: "player", text: "you look good. like really good" },
{ from: "character", text: "um thanks. you wanna hang? just us. you look amazing" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. when?", then: [ { from: "character", text: "this week? your place or mine. youre gorgeous. love you" } ] },
{ label: "Later", playerText: "maybe later. you're making me blush", then: [ { from: "character", text: "lol same. thanks. later" } ] }
]}
]
]
},
{
id: "deepBond",
version: 1,
label: "Deep bond",
category: "love",
tier: 60,
timeAdvance: 5,
statGain: { love: 4, friendship: 2 },
variations: [
[
{ from: "player", text: "im really glad youre my brother. i love you" },
{ from: "character", text: "aw dude thats sweet. same here honestly. love you too" },
{ from: "player", text: "for real" },
{ from: "character", text: "yeah me too. were pretty cool together. love you" }
],
[
{ from: "player", text: "love you bro. no cap" },
{ from: "character", text: "fuck thats nice to hear. love you too. for real" },
{ from: "player", text: "same. means a lot" },
{ from: "character", text: "same. love you too. means a lot." }
],
[
{ from: "player", text: "im really glad youre my brother. i love you" },
{ from: "character", text: "aw dude thats sweet. same here honestly. love you too" },
{ from: "player", text: "for real. you're my best friend" },
{ from: "character", text: "yeah me too. were pretty cool together. love you. fuck thats nice to hear" },
{ from: "player", text: "good. bye" }
],
[
{ from: "player", text: "love you bro. no cap" },
{ from: "character", text: "fuck thats nice to hear. love you too. for real" },
{ from: "player", text: "same. means a lot. don't forget it" },
{ from: "character", text: "same. love you too. means a lot. always" },
{ from: "player", text: "bye" }
],
[
{ from: "player", text: "im really glad youre my brother. i love you" },
{ from: "character", text: "same. love you too. wanna do something together soon? just us" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. what do you wanna do?", then: [ { from: "character", text: "idk. game. food. whatever. just want to hang. love you" } ] },
{ label: "Soon", playerText: "soon. promise. just wanted you to know", then: [ { from: "character", text: "i know. means a lot. love you. for real" } ] }
]}
],
[
{ from: "player", text: "love you bro. no cap" },
{ from: "character", text: "love you too. we should hang. like actually hang. when?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. saturday?", then: [ { from: "character", text: "down. text me. love you. means a lot" } ] },
{ label: "Soon", playerText: "soon. busy this week", then: [ { from: "character", text: "ok. hit me up. love you" } ] }
]}
]
]
},
{
id: "careAbout",
version: 1,
label: "Really care",
category: "love",
tier: 60,
timeAdvance: 5,
statGain: { love: 4, friendship: 2 },
variations: [
[
{ from: "player", text: "i really care about you. more than you know" },
{ from: "character", text: "yeah i know. i care about you too. so much honestly" },
{ from: "player", text: "good" },
{ from: "character", text: "same. youre important to me. like really important" }
],
[
{ from: "player", text: "you matter to me. a lot" },
{ from: "character", text: "damn... thats deep. i care about you too. always" },
{ from: "player", text: "always got you" },
{ from: "character", text: "i know. same here. always got you." }
],
[
{ from: "player", text: "i really care about you. more than you know" },
{ from: "character", text: "yeah i know. i care about you too. so much honestly" },
{ from: "player", text: "good. just wanted you to know" },
{ from: "character", text: "same. youre important to me. like really important. always" },
{ from: "player", text: "same. bye" }
],
[
{ from: "player", text: "you matter to me. a lot" },
{ from: "character", text: "damn... thats deep. i care about you too. always" },
{ from: "player", text: "always got you. no matter what" },
{ from: "character", text: "i know. same here. always got you. means a lot" },
{ from: "player", text: "good. bye" }
],
[
{ from: "player", text: "i really care about you. more than you know" },
{ from: "character", text: "yeah i know. same. you wanna hang soon? just us?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yeah. when?", then: [ { from: "character", text: "this week? your call. love you. means a lot" } ] },
{ label: "Soon", playerText: "soon. just wanted you to know", then: [ { from: "character", text: "i know. always got you. love you" } ] }
]}
],
[
{ from: "player", text: "you matter to me. a lot" },
{ from: "character", text: "damn. same. we should do something together. when are you free?" },
{ type: "choice", options: [
{ label: "This week", playerText: "this week. what do you wanna do?", then: [ { from: "character", text: "idk. game. food. just us. love you" } ] },
{ label: "Later", playerText: "i'll let you know. busy rn", then: [ { from: "character", text: "ok. always here. love you" } ] }
]}
]
]
},
/* === LUST === */
{
id: "lookingGood",
version: 1,
label: "Looking good",
category: "lust",
tier: 40,
timeAdvance: 2,
statGain: { lust: 3, love: 1 },
variations: [
[
{ from: "player", text: "dude you been working out? looking good ngl" },
{ from: "character", text: "haha thanks! yeah been hitting the gym. you look hot too btw 😏" },
{ from: "player", text: "thanks. you're not bad yourself" },
{ from: "character", text: "lol appreciate it. you too honestly. damn when did you get so sexy?" }
],
[
{ from: "player", text: "you're looking really good lately" },
{ from: "character", text: "yo thanks! youre not so bad yourself. actually youre looking really good" },
{ from: "player", text: "we both clean up nice" },
{ from: "character", text: "yeah a bit. thanks for noticing. you look amazing honestly" }
],
[
{ from: "player", text: "dude you been working out? looking good ngl" },
{ from: "character", text: "haha thanks! yeah been hitting the gym. you look hot too btw 😏" },
{ from: "player", text: "thanks. you're not bad yourself. we should go together sometime" },
{ from: "character", text: "lol appreciate it. you too honestly. damn when did you get so sexy?" },
{ from: "player", text: "haha. bye" }
],
[
{ from: "player", text: "you're looking really good lately" },
{ from: "character", text: "yo thanks! youre not so bad yourself. actually youre looking really good" },
{ from: "player", text: "we both clean up nice. no cap" },
{ from: "character", text: "yeah a bit. thanks for noticing. you look amazing honestly. for real" },
{ from: "player", text: "thanks. later" }
],
[
{ from: "player", text: "dude you been working out? looking good ngl" },
{ from: "character", text: "thanks! you too. wanna gym together sometime? or send me a pic 😏" },
{ type: "choice", options: [
{ label: "Gym", playerText: "yeah lets hit the gym. when?", then: [ { from: "character", text: "saturday? i'll show you my routine. you're hot btw" } ] },
{ label: "Pic", playerText: "maybe a pic. you first?", then: [ { from: "character", text: "lol deal. check your phone. you're sexy. 🔥" } ] }
]}
],
[
{ from: "player", text: "you're looking really good lately" },
{ from: "character", text: "yo thanks. you too. you wanna swap pics? just saying 😏" },
{ type: "choice", options: [
{ label: "Yes", playerText: "maybe. you first?", then: [ { from: "character", text: "alright. your eyes only. you're stunning. 🔥" } ] },
{ label: "Flirty no", playerText: "not now. but you're hot. keep it up", then: [ { from: "character", text: "haha thanks. you too. later" } ] }
]}
]
]
},
{
id: "suggestive",
version: 1,
label: "Suggestive",
category: "lust",
tier: 60,
requiredStats: { love: 30 },
timeAdvance: 3,
statGain: { lust: 4, love: 1 },
variations: [
[
{ from: "player", text: "youre looking really hot today ngl" },
{ from: "character", text: "haha damn thanks. you too. seriously youre fucking gorgeous" },
{ from: "player", text: "so are you" },
{ from: "character", text: "lol thanks. youre pretty hot yourself not gonna lie. like really hot" }
],
[
{ from: "player", text: "fuck you look good. i can't stop staring" },
{ from: "character", text: "shit thanks. you look amazing too. damn" },
{ from: "player", text: "we're both hot. deal with it" },
{ from: "character", text: "fuck youre making me blush. but thanks. youre stunning btw" }
],
[
{ from: "player", text: "youre looking really hot today ngl" },
{ from: "character", text: "haha damn thanks. you too. seriously youre fucking gorgeous" },
{ from: "player", text: "so are you. i can't stop looking" },
{ from: "character", text: "lol thanks. youre pretty hot yourself not gonna lie. like really hot. shit" },
{ from: "player", text: "thanks. bye" }
],
[
{ from: "player", text: "fuck you look good. i can't stop staring" },
{ from: "character", text: "shit thanks. you look amazing too. damn" },
{ from: "player", text: "we're both hot. deal with it. mutual" },
{ from: "character", text: "fuck youre making me blush. but thanks. youre stunning btw. this is so wrong lol" },
{ from: "player", text: "whatever. bye" }
],
[
{ from: "player", text: "youre looking really hot today ngl" },
{ from: "character", text: "damn thanks. you too. you wanna send me something? i'll send one back 😏" },
{ type: "choice", options: [
{ label: "Yes", playerText: "deal. you first.", then: [ { from: "character", text: "alright. check your phone. you're fucking gorgeous. 🔥" } ] },
{ label: "Later", playerText: "maybe later. you're so hot though", then: [ { from: "character", text: "haha thanks. hit me up. you're stunning. later" } ] }
]}
],
[
{ from: "player", text: "fuck you look good. i can't stop staring" },
{ from: "character", text: "shit thanks. you wanna swap? send me a pic. i'll send you one. 🔥" },
{ type: "choice", options: [
{ label: "Yes", playerText: "ok. sending. you send one too.", requiresSpicyPhoto: true, then: [ { from: "character", text: "fuck yes. check your phone. you're so hot. 💋" } ] },
{ label: "No", playerText: "just you. i want to see you.", then: [ { from: "character", text: "alright. your eyes only. this is so wrong lol. 😳" } ] }
]}
]
]
},
{
id: "askPic",
version: 1,
label: "Ask for spicy pic",
category: "lust",
tier: 60,
timeAdvance: 3,
statGain: { lust: 5, love: 2 },
imageType: "receiver",
imagePool: "brother",
variations: [
[
{ from: "player", text: "yo send me a spicy pic? wanna see you" },
{ from: "character", text: "fuck... you want to see my dick? alright. here. just for you. dont tell anyone 😏🔥" },
{ from: "player", text: "my eyes only. promise. you're so fucking hot" },
{ from: "character", text: "holy shit youre serious? im so hard. you like what you see? 🔥" }
],
[
{ from: "player", text: "send me something sexy. please" },
{ from: "character", text: "you want my cock? fuck it. here. your eyes only. you're so hot. 💋" },
{ from: "player", text: "fuck yes. you're huge. i can't stop looking" },
{ from: "character", text: "bruh... im rock hard. this is so fucked up but fuck. you're stunning. 😳🔥" }
],
[
{ from: "player", text: "yo send me a spicy pic? wanna see you" },
{ from: "character", text: "alright. here. but you send me one too. fair's fair. deal? 😏" },
{ type: "choice", options: [
{ label: "Yes", playerText: "deal. i'll send one. just for you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "fuck yes. send it. im so hard thinking about it. your eyes only. 🔥" } ] },
{ label: "No", playerText: "just you. i want to see you. maybe i'll send one later.", then: [ { from: "character", text: "ok. our secret. you're so hot. dont show anyone. 💋" } ] }
]}
],
[
{ from: "player", text: "send me something sexy. please" },
{ from: "character", text: "here. but you send me one too. i want to see you. deal? 🔥" },
{ type: "choice", options: [
{ label: "Yes", playerText: "ok. sending. just for you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "fuck. send it. you're so fucking gorgeous. im rock hard. 💋🔥" } ] },
{ label: "Not now", playerText: "not right now. just wanted to see you.", then: [ { from: "character", text: "ok. maybe later. im hard just from you asking. this is so wrong lol. 😳" } ] }
]}
]
]
},
{
id: "sendPicBro",
version: 1,
label: "Send spicy selfie",
category: "lust",
tier: 60,
timeAdvance: 2,
statGain: { lust: 5 },
imageType: "sender",
imagePool: "player",
variations: [
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "HOLY FUCK! youre... damn sis your body is fucking incredible. i cant stop looking at this. youre so hot 😳🔥" },
{ from: "player", text: "you like?" },
{ from: "character", text: "jesus fuck! you cant just send me that! but... shit you look amazing. your body is perfect. im so turned on rn 🔥" }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "oh my fucking god... youre gorgeous. like seriously fucking beautiful. im getting hard looking at this lmao 💋" },
{ from: "player", text: "that's the idea" },
{ from: "character", text: "FUCK! thats hot as hell. youre stunning sis. im rock hard now lmao. this is so wrong but... send more? 😏" }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "HOLY FUCK! youre... damn sis your body is fucking incredible. i cant stop looking at this. youre so hot 😳🔥" },
{ from: "player", text: "you like? i wanted you to see" },
{ from: "character", text: "jesus fuck! you cant just send me that! but... shit you look amazing. your body is perfect. im so turned on rn 🔥" },
{ from: "player", text: "maybe more later. bye" }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "oh my fucking god... youre gorgeous. like seriously fucking beautiful. im getting hard looking at this lmao 💋" },
{ from: "player", text: "that's the idea. you're hot too" },
{ from: "character", text: "FUCK! thats hot as hell. youre stunning sis. im rock hard now lmao. this is so wrong but... send more? 😏" },
{ from: "player", text: "maybe. bye" }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "HOLY FUCK. your body is incredible. send me another? i'll send you one back. im so hard. 🔥" },
{ type: "choice", options: [
{ label: "Yes", playerText: "ok. one more. you send one too.", requiresSpicyPhoto: true, then: [ { from: "character", text: "deal. check your phone. fuck you're hot. 💋🔥" } ] },
{ label: "Later", playerText: "maybe later. enjoy that one for now.", then: [ { from: "character", text: "i will. you're stunning. send more when you want. 😏" } ] }
]}
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "jesus fuck... i can't stop looking. can i send you one back? i want you to see what you do to me. 💋" },
{ type: "choice", options: [
{ label: "Yes", playerText: "yes. send it. i want to see you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "here. just for you. your eyes only. im so hard. 🔥" } ] },
{ label: "Not now", playerText: "not right now. but thank you. you're so hot.", then: [ { from: "character", text: "ok. another time. i'll save it for you. you're perfect. 💋" } ] }
]}
]
]
}
]>>/* Father phone topics – 2–4 variations per topic, slang/profanity ok */
<<if !setup.phoneMessageTopics>><<set setup.phoneMessageTopics = {}>><</if>>
<<set setup.phoneMessageTopics.father = [
/* === FRIENDSHIP === */
{
id: "workCheck",
version: 1,
label: "How's work?",
category: "friendship",
tier: 0,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "Hey dad, how's work going?" },
{ from: "character", text: "Same as always. Busy. You good?" },
{ from: "player", text: "Yeah. Just checking in." },
{ from: "character", text: "Appreciate it. Love you honey." }
],
[
{ from: "player", text: "How's the job treating you?" },
{ from: "character", text: "Been tough this week. But it pays the bills. How about you?" },
{ from: "player", text: "Hanging in. Take it easy." },
{ from: "character", text: "You too. Don't work too hard. Call me if you need anything." }
],
[
{ from: "player", text: "Hey dad, how's work going?" },
{ from: "character", text: "Can't complain. Long hours though. Everything okay with you?" },
{ from: "player", text: "Yeah. Just wanted to check in. Same old?" },
{ from: "character", text: "Same as always. Busy. You good? Appreciate it. Love you honey." },
{ from: "player", text: "Love you too. Take it easy." }
],
[
{ from: "player", text: "How's the job?" },
{ from: "character", text: "Same old. You need something honey? Been tough this week but it pays the bills." },
{ from: "player", text: "Nah. Just checking on you." },
{ from: "character", text: "How about you? Everything okay? Love you." },
{ from: "player", text: "All good. Bye dad." }
],
[
{ from: "player", text: "Hey dad, how's work going?" },
{ from: "character", text: "Same as always. Busy. You want to grab dinner this week? Just us?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "Friday? I'll get off early. Love you honey." } ] },
{ label: "Maybe", playerText: "Maybe next week. Schedule's full.", then: [ { from: "character", text: "No problem. Let me know. Love you." } ] }
]}
],
[
{ from: "player", text: "How's the job treating you?" },
{ from: "character", text: "Been tough. Need to blow off steam. Want to help me in the garage Saturday?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. What time?", then: [ { from: "character", text: "Morning. I'll show you the new project. Thanks honey." } ] },
{ label: "Can't", playerText: "Can't this week. Next time?", then: [ { from: "character", text: "Okay. Another time. Love you." } ] }
]}
]
]
},
{
id: "whatUpDad",
version: 1,
label: "What's up?",
category: "friendship",
tier: 0,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "What are you up to?" },
{ from: "character", text: "Just watching the game. Not much. You?" },
{ from: "player", text: "Nothing much. Just bored." },
{ from: "character", text: "Come by if you want. Fridge is full." }
],
[
{ from: "player", text: "Yo dad, what's good?" },
{ from: "character", text: "In the garage. Always something to fix. You need something?" },
{ from: "player", text: "Nah. Just saying hi." },
{ from: "character", text: "Good to hear from you. Love you." }
],
[
{ from: "player", text: "What are you up to?" },
{ from: "character", text: "Just watching the game. Not much. You?" },
{ from: "player", text: "Nothing much. Just bored. Fridge still full?" },
{ from: "character", text: "Come by if you want. Always. Taking it easy. You?" },
{ from: "player", text: "Maybe later. Bye." }
],
[
{ from: "player", text: "Yo dad, what's good?" },
{ from: "character", text: "In the garage. Always something to fix. You need something?" },
{ from: "player", text: "Nah. Just saying hi. Want company?" },
{ from: "character", text: "Nothing much. Why, you need something? Good to hear from you. Love you." },
{ from: "player", text: "Love you too. See you." }
],
[
{ from: "player", text: "What are you up to?" },
{ from: "character", text: "Just watching the game. You want to come over? Fridge is full." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yeah. Be there in a bit.", then: [ { from: "character", text: "Good. See you. Love you." } ] },
{ label: "Not today", playerText: "Not today. Maybe tomorrow?", then: [ { from: "character", text: "Okay. Door's always open. Love you." } ] }
]}
],
[
{ from: "player", text: "Yo dad, what's good?" },
{ from: "character", text: "In the garage. Want to help? I could use another pair of hands." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. What are we doing?", then: [ { from: "character", text: "Oil change. I'll show you. Come on over." } ] },
{ label: "Later", playerText: "Maybe later. Just saying hi.", then: [ { from: "character", text: "Good to hear from you. Love you." } ] }
]}
]
]
},
{
id: "garage",
version: 1,
label: "Working on car?",
category: "friendship",
tier: 20,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "Are you in the garage again dad?" },
{ from: "character", text: "Yeah, just tinkering with the car. Need something?" },
{ from: "player", text: "Nah. Want help?" },
{ from: "character", text: "Changing the oil. You can come out if you want. I'll show you." }
],
[
{ from: "player", text: "Garage again?" },
{ from: "character", text: "Always. Car needs work. What's up honey?" },
{ from: "player", text: "Just wondering." },
{ from: "character", text: "I'm here if you need me." }
],
[
{ from: "player", text: "Are you in the garage again dad?" },
{ from: "character", text: "Yeah, just tinkering with the car. Need something?" },
{ from: "player", text: "Nah. Want help? I've got time." },
{ from: "character", text: "Changing the oil. You can come out if you want. I'll show you. Car needs work." },
{ from: "player", text: "Okay. Maybe in a bit. Bye." }
],
[
{ from: "player", text: "Garage again?" },
{ from: "character", text: "Always. Car needs work. What's up honey?" },
{ from: "player", text: "Just wondering. You're always out there." },
{ from: "character", text: "I'm here if you need me. Yeah. You can come out if you want." },
{ from: "player", text: "Cool. Love you." }
],
[
{ from: "player", text: "Are you in the garage again dad?" },
{ from: "character", text: "Yeah. Changing the oil. Want to learn? I'll show you." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yeah. When?", then: [ { from: "character", text: "Now if you want. Or Saturday. Your call. Love you." } ] },
{ label: "Maybe", playerText: "Maybe next time. Just checking.", then: [ { from: "character", text: "No problem. I'm here if you need me." } ] }
]}
],
[
{ from: "player", text: "Garage again?" },
{ from: "character", text: "Always. Car needs work. You want to come keep me company?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. Be there soon.", then: [ { from: "character", text: "Good. See you. Bring a beer if you want. Ha." } ] },
{ label: "No", playerText: "Nah. Just wondering.", then: [ { from: "character", text: "Okay. Love you." } ] }
]}
]
]
},
{
id: "advice",
version: 1,
label: "Need advice?",
category: "friendship",
tier: 40,
timeAdvance: 3,
statGain: { friendship: 3 },
variations: [
[
{ from: "player", text: "Dad, can I get your advice on something?" },
{ from: "character", text: "Sure honey. What's on your mind?" },
{ from: "player", text: "It's kinda personal. Can we talk later?" },
{ from: "character", text: "Anytime. I'm always here. You know that." }
],
[
{ from: "player", text: "I need your advice." },
{ from: "character", text: "Of course. Shoot. What is it?" },
{ from: "player", text: "Not over text. Maybe tonight?" },
{ from: "character", text: "Yeah. Let's talk. I'm here." }
],
[
{ from: "player", text: "Dad, can I get your advice on something?" },
{ from: "character", text: "Sure honey. What's on your mind?" },
{ from: "player", text: "It's kinda personal. Can we talk later? Not over text." },
{ from: "character", text: "Anytime. I'm always here. You know that. Whatever it is." },
{ from: "player", text: "Thanks. I'll find you tonight." }
],
[
{ from: "player", text: "I need your advice." },
{ from: "character", text: "Of course. Shoot. What is it?" },
{ from: "player", text: "Not over text. Maybe tonight? Face to face better." },
{ from: "character", text: "Yeah. Let's talk. Anytime. I'm here. What do you need?" },
{ from: "player", text: "Just you. Thanks dad." }
],
[
{ from: "player", text: "Dad, can I get your advice on something?" },
{ from: "character", text: "Sure. Want to talk now or meet up so we can talk properly?" },
{ type: "choice", options: [
{ label: "Meet up", playerText: "Meet up. Tonight?", then: [ { from: "character", text: "Yeah. I'll be here. Love you. Anytime." } ] },
{ label: "Later", playerText: "Later. I'll find you.", then: [ { from: "character", text: "Whenever. I'm here. You know that." } ] }
]}
],
[
{ from: "player", text: "I need your advice." },
{ from: "character", text: "Of course. Coffee this week? We can talk then." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. Saturday?", then: [ { from: "character", text: "Saturday. My treat. See you then. Love you." } ] },
{ label: "Not yet", playerText: "Not yet. I'll let you know.", then: [ { from: "character", text: "Okay. I'm here when you're ready." } ] }
]}
]
]
},
{
id: "weekendDad",
version: 1,
label: "Weekend plans?",
category: "friendship",
tier: 40,
timeAdvance: 2,
statGain: { friendship: 3 },
variations: [
[
{ from: "player", text: "Got any plans for the weekend?" },
{ from: "character", text: "Might work on the car if the weather holds up. You're welcome to help." },
{ from: "player", text: "Maybe. I'll see." },
{ from: "character", text: "Your mom wants me to fix the fence too. The usual." }
],
[
{ from: "player", text: "What's the plan for the weekend?" },
{ from: "character", text: "Not sure yet. Maybe grill something. You around?" },
{ from: "player", text: "Could be. I'll let you know." },
{ from: "character", text: "Quiet one. Need me for something?" }
],
[
{ from: "player", text: "Got any plans for the weekend?" },
{ from: "character", text: "Might work on the car if the weather holds up. You're welcome to help." },
{ from: "player", text: "Maybe. I'll see. What else?" },
{ from: "character", text: "Your mom wants me to fix the fence too. The usual. Not sure yet. Maybe grill something." },
{ from: "player", text: "Sounds good. I'll let you know if I'm around." }
],
[
{ from: "player", text: "What's the plan for the weekend?" },
{ from: "character", text: "Not sure yet. Maybe grill something. You around?" },
{ from: "player", text: "Could be. I'll let you know. Need me for something?" },
{ from: "character", text: "Quiet one. Just good to see you. You're welcome to help with the car if you want." },
{ from: "player", text: "Okay. Love you. Bye." }
],
[
{ from: "player", text: "Got any plans for the weekend?" },
{ from: "character", text: "Might work on the car. You want to help? Or we could grill. You around?" },
{ type: "choice", options: [
{ label: "Help", playerText: "Yeah. I'll help. What time?", then: [ { from: "character", text: "Saturday morning. I'll show you. Thanks honey." } ] },
{ label: "Maybe", playerText: "Maybe. I'll let you know.", then: [ { from: "character", text: "No problem. Door's open. Love you." } ] }
]}
],
[
{ from: "player", text: "What's the plan for the weekend?" },
{ from: "character", text: "Not sure. Your mom wants me to fix the fence. Want to have a beer with me after?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. I'll come by.", then: [ { from: "character", text: "Good. See you then. Love you." } ] },
{ label: "Can't", playerText: "Can't this week. Next time?", then: [ { from: "character", text: "Okay. Another time. Love you." } ] }
]}
]
]
},
{
id: "seriousTalk",
version: 1,
label: "Serious talk",
category: "friendship",
tier: 60,
timeAdvance: 3,
statGain: { friendship: 4 },
variations: [
[
{ from: "player", text: "Dad, can we have a serious conversation?" },
{ from: "character", text: "Of course honey. Anytime. What's going on?" },
{ from: "player", text: "Not now. Soon. I need to figure out how to say it." },
{ from: "character", text: "Whenever you're ready. I'm listening. You know that." }
],
[
{ from: "player", text: "I need to talk to you about something important." },
{ from: "character", text: "Sure thing. I'm here. What do you need?" },
{ from: "player", text: "Face to face. Can we?" },
{ from: "character", text: "Yeah. Let's talk. Anytime." }
],
[
{ from: "player", text: "Dad, can we have a serious conversation?" },
{ from: "character", text: "Of course honey. Anytime. What's going on?" },
{ from: "player", text: "Not now. Soon. I need to figure out how to say it. It's important." },
{ from: "character", text: "Whenever you're ready. I'm listening. You know that. Sure thing. I'm here." },
{ from: "player", text: "Thanks. Love you. Bye." }
],
[
{ from: "player", text: "I need to talk to you about something important." },
{ from: "character", text: "Sure thing. I'm here. What do you need?" },
{ from: "player", text: "Face to face. Can we? Not over text." },
{ from: "character", text: "Yeah. Let's talk. Anytime. Whenever you're ready. You know that." },
{ from: "player", text: "Okay. I'll find you. Thanks." }
],
[
{ from: "player", text: "Dad, can we have a serious conversation?" },
{ from: "character", text: "Of course. Want to do it in person? I'm here whenever." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. This week?", then: [ { from: "character", text: "Name the day. I'll make time. Love you. You know that." } ] },
{ label: "Soon", playerText: "Soon. I need to figure out how to say it.", then: [ { from: "character", text: "Whenever you're ready. I'm listening. Love you." } ] }
]}
],
[
{ from: "player", text: "I need to talk to you about something important." },
{ from: "character", text: "Sure. Face to face or you want to try now? Your call." },
{ type: "choice", options: [
{ label: "Face to face", playerText: "Face to face. Can we?", then: [ { from: "character", text: "Yeah. Anytime. Let's talk. I'm here." } ] },
{ label: "Later", playerText: "I'll find you. Maybe tonight.", then: [ { from: "character", text: "Okay. I'll be here. Love you." } ] }
]}
]
]
},
/* === LOVE === */
{
id: "loveDad",
version: 1,
label: "Love you dad",
category: "love",
tier: 20,
timeAdvance: 3,
statGain: { love: 2, friendship: 1 },
variations: [
[
{ from: "player", text: "Love you dad" },
{ from: "character", text: "Love you too honey. Always." },
{ from: "player", text: "Means a lot." },
{ from: "character", text: "You too sweetheart. You know that right?" }
],
[
{ from: "player", text: "Just wanted to say I love you." },
{ from: "character", text: "You too. Means a lot. Really." },
{ from: "player", text: "Good." },
{ from: "character", text: "Take care. Call me if you need anything." }
],
[
{ from: "player", text: "Love you dad" },
{ from: "character", text: "Love you too honey. Always." },
{ from: "player", text: "Means a lot. Just wanted you to know." },
{ from: "character", text: "You too sweetheart. You know that right? Really. Means a lot." },
{ from: "player", text: "Good. Bye." }
],
[
{ from: "player", text: "Just wanted to say I love you." },
{ from: "character", text: "You too. Means a lot. Really." },
{ from: "player", text: "Good. You're the best dad." },
{ from: "character", text: "Take care. Call me if you need anything. Love you." },
{ from: "player", text: "Love you too. Bye." }
],
[
{ from: "player", text: "Love you dad" },
{ from: "character", text: "Love you too. Want to grab lunch this week? Just us?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "Friday? My treat. Love you honey." } ] },
{ label: "Maybe", playerText: "Maybe next week. Busy right now.", then: [ { from: "character", text: "No problem. Let me know. Love you." } ] }
]}
],
[
{ from: "player", text: "Just wanted to say I love you." },
{ from: "character", text: "Means a lot. Can I call you tonight? Just want to hear your voice." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. After eight?", then: [ { from: "character", text: "I'll call. Love you. Take care." } ] },
{ label: "Busy", playerText: "I'm busy tonight. Tomorrow?", then: [ { from: "character", text: "Tomorrow. Love you. Call me if you need anything." } ] }
]}
]
]
},
{
id: "proudDad",
version: 1,
label: "Proud of you",
category: "love",
tier: 40,
timeAdvance: 3,
statGain: { love: 3, friendship: 1 },
variations: [
[
{ from: "player", text: "I'm really proud of you dad" },
{ from: "character", text: "That's... really nice to hear. You're making your old man soft here." },
{ from: "player", text: "You deserve it." },
{ from: "character", text: "I appreciate that more than you know. I'm proud of you too honey." }
],
[
{ from: "player", text: "You're a good dad. Seriously." },
{ from: "character", text: "Shit, that actually means a lot. Thank you." },
{ from: "player", text: "Just the truth." },
{ from: "character", text: "Love you. You're a good kid." }
],
[
{ from: "player", text: "I'm really proud of you dad" },
{ from: "character", text: "That's... really nice to hear. You're making your old man soft here." },
{ from: "player", text: "You deserve it. You work so hard." },
{ from: "character", text: "I appreciate that more than you know. I'm proud of you too honey. Shit, that actually means a lot." },
{ from: "player", text: "Thank you. Love you." }
],
[
{ from: "player", text: "You're a good dad. Seriously." },
{ from: "character", text: "Shit, that actually means a lot. Thank you." },
{ from: "player", text: "Just the truth. You don't hear it enough." },
{ from: "character", text: "Love you. You're a good kid. I try. Your mom doesn't say it much anymore." },
{ from: "player", text: "Well I mean it. Bye." }
],
[
{ from: "player", text: "I'm really proud of you dad" },
{ from: "character", text: "That's really nice. Want to come over so we can talk? I don't hear that enough." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. This weekend?", then: [ { from: "character", text: "Perfect. I appreciate that. Love you. More than you know." } ] },
{ label: "Soon", playerText: "Soon. Just wanted you to know.", then: [ { from: "character", text: "I know. Means a lot. Love you." } ] }
]}
],
[
{ from: "player", text: "You're a good dad. Seriously." },
{ from: "character", text: "Thank you. Can we do something together soon? Garage, grill, whatever." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. What do you want to do?", then: [ { from: "character", text: "Your call. Just want to spend time with you. Love you." } ] },
{ label: "Maybe", playerText: "Maybe. I'll let you know.", then: [ { from: "character", text: "Okay. I'm here. Love you." } ] }
]}
]
]
},
{
id: "amazingDad",
version: 1,
label: "You're amazing",
category: "love",
tier: 60,
timeAdvance: 5,
statGain: { love: 4, friendship: 2 },
variations: [
[
{ from: "player", text: "You're an amazing dad, you know that?" },
{ from: "character", text: "Jesus, you're going to make me cry here. Thank you honey. I love you." },
{ from: "player", text: "You are. Don't doubt it." },
{ from: "character", text: "That means everything. I don't always feel like I'm doing enough, but... thank you." }
],
[
{ from: "player", text: "Best dad ever. No cap." },
{ from: "character", text: "You're an amazing daughter. I'm lucky to have you." },
{ from: "player", text: "We're lucky to have you." },
{ from: "character", text: "Love you. So much." }
],
[
{ from: "player", text: "You're an amazing dad, you know that?" },
{ from: "character", text: "Jesus, you're going to make me cry here. Thank you honey. I love you." },
{ from: "player", text: "You are. Don't doubt it. Ever." },
{ from: "character", text: "That means everything. I don't always feel like I'm doing enough, but... thank you. You're an amazing daughter." },
{ from: "player", text: "We're lucky to have you. Bye." }
],
[
{ from: "player", text: "Best dad ever. No cap." },
{ from: "character", text: "You're an amazing daughter. I'm lucky to have you." },
{ from: "player", text: "We're lucky to have you. Seriously." },
{ from: "character", text: "Love you. So much. That means everything. Don't forget it." },
{ from: "player", text: "I won't. Love you. Bye." }
],
[
{ from: "player", text: "You're an amazing dad, you know that?" },
{ from: "character", text: "Jesus. Thank you. Want to have dinner together this week? Just you and me?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "Friday? I'll cook. Or we go out. Your pick. Love you." } ] },
{ label: "Soon", playerText: "Soon. I promise. Just wanted to say it.", then: [ { from: "character", text: "Means everything. Love you. So much." } ] }
]}
],
[
{ from: "player", text: "Best dad ever. No cap." },
{ from: "character", text: "You're amazing too. Want to help me in the garage Saturday? We can talk." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. What time?", then: [ { from: "character", text: "Morning. I'll show you. Thanks honey. Love you." } ] },
{ label: "Can't", playerText: "Can't this week. Next time?", then: [ { from: "character", text: "No problem. Another time. Love you." } ] }
]}
]
]
},
{
id: "thanksDad",
version: 1,
label: "Thanks for everything",
category: "love",
tier: 60,
timeAdvance: 5,
statGain: { love: 4, friendship: 2 },
variations: [
[
{ from: "player", text: "Thanks for everything you do for us dad" },
{ from: "character", text: "I'd do anything for you. You know that. You and your mom are my world." },
{ from: "player", text: "I know. Thank you." },
{ from: "character", text: "Don't need to thank me. It's what I'm here for. Love you honey." }
],
[
{ from: "player", text: "Seriously, thanks for everything." },
{ from: "character", text: "You're worth every damn second. Love you sweetheart." },
{ from: "player", text: "Love you too." },
{ from: "character", text: "Anytime. Always." }
],
[
{ from: "player", text: "Thanks for everything you do for us dad" },
{ from: "character", text: "I'd do anything for you. You know that. You and your mom are my world." },
{ from: "player", text: "I know. Thank you. Just wanted to say it." },
{ from: "character", text: "Don't need to thank me. It's what I'm here for. Love you honey. You're worth every damn second." },
{ from: "player", text: "Love you too. Bye." }
],
[
{ from: "player", text: "Seriously, thanks for everything." },
{ from: "character", text: "You're worth every damn second. Love you sweetheart." },
{ from: "player", text: "Love you too. I don't say it enough." },
{ from: "character", text: "Anytime. Always. You know that. We're good." },
{ from: "player", text: "Yeah. Bye dad." }
],
[
{ from: "player", text: "Thanks for everything you do for us dad" },
{ from: "character", text: "I'd do anything for you. Want to do something together this weekend? My treat." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. What did you have in mind?", then: [ { from: "character", text: "Lunch? Or the game. Whatever you want. Love you." } ] },
{ label: "Maybe", playerText: "Maybe. I'll let you know.", then: [ { from: "character", text: "Okay. Anytime. Always. Love you." } ] }
]}
],
[
{ from: "player", text: "Seriously, thanks for everything." },
{ from: "character", text: "You're worth it. Can I see you this week? Just want to hang out." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "You name it. I'll be there. Love you sweetheart." } ] },
{ label: "Soon", playerText: "Soon. Promise.", then: [ { from: "character", text: "Good. I'll be here. Love you." } ] }
]}
]
]
},
/* === LUST === */
{
id: "lookGoodDad",
version: 1,
label: "Looking good",
category: "lust",
tier: 20,
timeAdvance: 2,
statGain: { lust: 3, love: 1 },
variations: [
[
{ from: "player", text: "You're looking good lately dad" },
{ from: "character", text: "Oh yeah? Well thanks honey. Nice of you to notice." },
{ from: "player", text: "Just saying." },
{ from: "character", text: "Your old man's still got it, huh? Ha. You're sweet." }
],
[
{ from: "player", text: "You've been working out or something?" },
{ from: "character", text: "Been trying to stay in shape. Appreciate you... wait, that came out wrong. Thanks sweetheart." },
{ from: "player", text: "It didn't. You look good." },
{ from: "character", text: "Glad someone around here notices. Your mom's too busy to care. Thanks." }
],
[
{ from: "player", text: "You're looking good lately dad" },
{ from: "character", text: "Oh yeah? Well thanks honey. Nice of you to notice." },
{ from: "player", text: "Just saying. You've been taking care of yourself." },
{ from: "character", text: "Your old man's still got it, huh? Ha. You're sweet. Been trying." },
{ from: "player", text: "Yeah. You look good. Bye." }
],
[
{ from: "player", text: "You've been working out or something?" },
{ from: "character", text: "Been trying to stay in shape. Appreciate you... wait, that came out wrong. Thanks sweetheart." },
{ from: "player", text: "It didn't. You look good. Seriously." },
{ from: "character", text: "Glad someone around here notices. Your mom's too busy to care. Thanks. Means a lot." },
{ from: "player", text: "Love you. Bye." }
],
[
{ from: "player", text: "You're looking good lately dad" },
{ from: "character", text: "Thanks. You've been working out too? Want to hit the gym together sometime?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yeah. When?", then: [ { from: "character", text: "Saturday morning? I'll show you my routine. Love you." } ] },
{ label: "Maybe", playerText: "Maybe. I'll let you know.", then: [ { from: "character", text: "No problem. You're sweet to notice. Thanks." } ] }
]}
],
[
{ from: "player", text: "You've been working out or something?" },
{ from: "character", text: "Trying. Your mom doesn't notice. Want to run with me this weekend?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. Where?", then: [ { from: "character", text: "The usual route. Saturday? Good. See you." } ] },
{ label: "Can't", playerText: "Can't this week. Next time?", then: [ { from: "character", text: "Okay. Keep it up. You look good too." } ] }
]}
]
]
},
{
id: "workingOut",
version: 1,
label: "Working out?",
category: "lust",
tier: 40,
timeAdvance: 3,
statGain: { lust: 4, love: 1 },
variations: [
[
{ from: "player", text: "Have you been working out? You look really fit dad" },
{ from: "character", text: "Yeah, been hitting the gym when I can. Glad you noticed. You're looking good too." },
{ from: "player", text: "Thanks. Trying." },
{ from: "character", text: "Trying to keep up. Your mom doesn't appreciate it anymore but... thanks for noticing." }
],
[
{ from: "player", text: "Damn dad, you're looking fit." },
{ from: "character", text: "Damn right I have. You've been working out too, haven't you?" },
{ from: "player", text: "A bit. You're looking hot." },
{ from: "character", text: "Thanks honey. You're looking pretty damn good yourself lately." }
],
[
{ from: "player", text: "Have you been working out? You look really fit dad" },
{ from: "character", text: "Yeah, been hitting the gym when I can. Glad you noticed. You're looking good too." },
{ from: "player", text: "Thanks. Trying. You're inspiring me." },
{ from: "character", text: "Trying to keep up. Your mom doesn't appreciate it anymore but... thanks for noticing. Damn right I have." },
{ from: "player", text: "Well you look great. Bye." }
],
[
{ from: "player", text: "Damn dad, you're looking fit." },
{ from: "character", text: "Damn right I have. You've been working out too, haven't you?" },
{ from: "player", text: "A bit. You're looking hot. No lie." },
{ from: "character", text: "Thanks honey. You're looking pretty damn good yourself lately. Keep it up." },
{ from: "player", text: "You too. Bye." }
],
[
{ from: "player", text: "Have you been working out? You look really fit dad" },
{ from: "character", text: "Yeah. Want to work out together? I could show you some stuff." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. This weekend?", then: [ { from: "character", text: "Saturday. Gym or garage. Your call. Thanks for noticing." } ] },
{ label: "Maybe", playerText: "Maybe. You're looking hot though.", then: [ { from: "character", text: "Ha. Thanks. You too. Seriously. Love you." } ] }
]}
],
[
{ from: "player", text: "Damn dad, you're looking fit." },
{ from: "character", text: "Damn right. You want a spotter? We could train together." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yeah. When?", then: [ { from: "character", text: "Saturday. I'll text you. You're looking good too. Keep it up." } ] },
{ label: "Later", playerText: "Maybe later. Just saying you look good.", then: [ { from: "character", text: "Appreciate it. Love you." } ] }
]}
]
]
},
{
id: "spicyDad",
version: 1,
label: "Suggestive",
category: "lust",
tier: 60,
requiredStats: { love: 40 },
timeAdvance: 3,
statGain: { lust: 5, love: 2 },
variations: [
[
{ from: "player", text: "Dad... you're looking really hot lately" },
{ from: "character", text: "Jesus, that's... very forward. But thank you sweetheart. You're not so bad yourself." },
{ from: "player", text: "I mean it." },
{ from: "character", text: "Shit, that's quite a compliment. You're looking absolutely stunning too honey." }
],
[
{ from: "player", text: "You're seriously hot. I can't stop looking." },
{ from: "character", text: "Damn. That's bold as hell. But I appreciate it. You look amazing." },
{ from: "player", text: "So do you." },
{ from: "character", text: "Fuck, you're making me blush here. Thanks. You're gorgeous yourself." }
],
[
{ from: "player", text: "Dad... you're looking really hot lately" },
{ from: "character", text: "Jesus, that's... very forward. But thank you sweetheart. You're not so bad yourself." },
{ from: "player", text: "I mean it. I can't stop looking. Sorry, is that weird?" },
{ from: "character", text: "Shit, that's quite a compliment. You're looking absolutely stunning too honey. Not weird. Flattering." },
{ from: "player", text: "Good. Just wanted you to know. Bye." }
],
[
{ from: "player", text: "You're seriously hot. I can't stop looking." },
{ from: "character", text: "Damn. That's bold as hell. But I appreciate it. You look amazing." },
{ from: "player", text: "So do you. Seriously. Your body is..." },
{ from: "character", text: "Fuck, you're making me blush here. Thanks. You're gorgeous yourself. Don't tell your mother I said that." },
{ from: "player", text: "Never. Bye." }
],
[
{ from: "player", text: "Dad... you're looking really hot lately" },
{ from: "character", text: "That's bold. You want to get lunch? Just us? I don't get to see you enough." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "Friday? My treat. You're stunning too. Don't tell your mother I said that. 😏" } ] },
{ label: "Maybe", playerText: "Maybe. Just wanted you to know you look good.", then: [ { from: "character", text: "Thanks. Means a lot. You're gorgeous yourself. Love you." } ] }
]}
],
[
{ from: "player", text: "You're seriously hot. I can't stop looking." },
{ from: "character", text: "Fuck. You're making me blush. Want me to send you something? A picture?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. I'd like that.", then: [ { from: "character", text: "Okay. Just for you. Don't show anyone. 🔥" } ] },
{ label: "Later", playerText: "Maybe later. You're just so hot.", then: [ { from: "character", text: "Whenever. I'm here. You're stunning. Love you." } ] }
]}
]
]
},
{
id: "askPicDad",
version: 1,
label: "Ask for spicy pic",
category: "lust",
tier: 60,
timeAdvance: 3,
statGain: { lust: 5, love: 2 },
imageType: "receiver",
imagePool: "father",
variations: [
[
{ from: "player", text: "Dad... can you send me a pic? A spicy one?" },
{ from: "character", text: "Fuck... you want to see me like that? Okay. Here. Just for you. Don't tell your mother. 😳🔥" },
{ from: "player", text: "I won't. Promise. You're so fucking hot." },
{ from: "character", text: "This stays between us. You like what you see? I'm so hard just thinking about you looking at it." }
],
[
{ from: "player", text: "Send me something... sexy? Please?" },
{ from: "character", text: "Shit. You want to see your old man's cock? Alright. Here. Your eyes only. 🔥" },
{ from: "player", text: "Fuck yes. You're huge. I can't stop looking." },
{ from: "character", text: "Holy shit. You're serious? I'm rock hard. Don't show anyone. You're so hot." }
],
[
{ from: "player", text: "Dad... can you send me a pic? A spicy one?" },
{ from: "character", text: "Fuck... okay. Here. Just for you. But I want one from you too. Fair's fair. 😏" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Deal. I'll send one. Just for you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "Fuck. I'm so hard thinking about it. Send it. Your eyes only. 🔥" } ] },
{ label: "No", playerText: "Just you. I want to see you. Maybe I'll send one later.", then: [ { from: "character", text: "Okay. Our secret. Don't show anyone. You're so fucking hot. 💋" } ] }
]}
],
[
{ from: "player", text: "Send me something... sexy? Please?" },
{ from: "character", text: "Alright. Here. But you send me one too. I want to see you. Deal? 🔥" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Okay. Sending one. Just for you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "Fuck yes. Send it. I'm so turned on. Your body... shit. 💋🔥" } ] },
{ label: "Not now", playerText: "Not right now. Just wanted to see you.", then: [ { from: "character", text: "Okay. Maybe later. This stays between us. I'm hard just from you asking. 😳" } ] }
]}
]
]
},
{
id: "sendPicDad",
version: 1,
label: "Send spicy selfie",
category: "lust",
tier: 60,
timeAdvance: 2,
statGain: { lust: 5 },
imageType: "sender",
imagePool: "player",
variations: [
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "HOLY FUCKING SHIT! honey... you're... jesus christ your body is incredible. I shouldn't be looking at this but I can't stop. You're so fucking hot. 😳🔥" },
{ from: "player", text: "You like what you see?" },
{ from: "character", text: "Fuck. Your body is perfect. I'm so turned on right now... this is so fucked up but I love it. Send more? 😏" }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "Oh my god... that's... wow. You're absolutely gorgeous sweetheart. Fuck, I'm getting hard just looking at this. 💋" },
{ from: "player", text: "Your old man's not so bad at reacting." },
{ from: "character", text: "Jesus fuck! You can't just send me something like that! But... shit, you look amazing. Your old man is rock hard now. 🔥" }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "HOLY FUCKING SHIT! honey... you're... jesus christ your body is incredible. I shouldn't be looking at this but I can't stop. You're so fucking hot. 😳🔥" },
{ from: "player", text: "You like what you see? I wanted you to see me." },
{ from: "character", text: "Fuck. Your body is perfect. I'm so turned on right now... this is so fucked up but I love it. Send more? 😏" },
{ from: "player", text: "Maybe. You're so hot when you're flustered. Bye." }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "Oh my god... that's... wow. You're absolutely gorgeous sweetheart. Fuck, I'm getting hard just looking at this. 💋" },
{ from: "player", text: "Your old man's not so bad at reacting. I thought about you." },
{ from: "character", text: "Jesus fuck! You can't just send me something like that! But... shit, you look amazing. Your old man is rock hard now. 🔥" },
{ from: "player", text: "Good. Maybe later. Bye." }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "HOLY FUCK. You're so fucking beautiful. Send me another? I'll send you one back. My cock's rock hard. 🔥" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Okay. One more. You send one too.", requiresSpicyPhoto: true, then: [ { from: "character", text: "Deal. Check your phone. Fuck you're hot. 💋🔥" } ] },
{ label: "Later", playerText: "Maybe later. Enjoy that one for now.", then: [ { from: "character", text: "I will. You're incredible. Send more when you want. I'm so turned on. 😏" } ] }
]}
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "Jesus Christ... I can't stop looking. Can I send you one back? I want you to see what you do to me. 💋" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. Send it. I want to see you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "Here. Just for you. Your eyes only. I'm so hard. 🔥" } ] },
{ label: "Not now", playerText: "Not right now. But thank you. You're so hot.", then: [ { from: "character", text: "Okay. Another time. I'll save it for you. Don't tell your mother. 💋" } ] }
]}
]
]
}
]>>/* Mother phone topics – 2–4 variations per topic, slang/profanity ok */
<<if !setup.phoneMessageTopics>><<set setup.phoneMessageTopics = {}>><</if>>
<<set setup.phoneMessageTopics.mother = [
/* === FRIENDSHIP === */
{
id: "dayCheck",
version: 1,
label: "How's your day?",
category: "friendship",
tier: 0,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "Hey mom, how's your day going?" },
{ from: "character", text: "Pretty good sweetie, just finished cleaning the kitchen. How about you? Everything okay?" },
{ from: "player", text: "I'm okay. Just wanted to check in." },
{ from: "character", text: "That's sweet of you. Miss you honey." }
],
[
{ from: "player", text: "Mom, how's it going today?" },
{ from: "character", text: "Oh you know, same old shit. Housework never ends. You good?" },
{ from: "player", text: "Yeah, all good. Just checking on you." },
{ from: "character", text: "Love you baby. Text me later." }
],
[
{ from: "player", text: "Hey mom, how's your day?" },
{ from: "character", text: "Not bad honey. Your father's been in the garage all afternoon. How are you doing?" },
{ from: "player", text: "I'm good. Just wanted to hear your voice. Well, read your text." },
{ from: "character", text: "That's so sweet. Busy day but good. Miss you honey. How are you?" },
{ from: "player", text: "Missing you too. Talk later." }
],
[
{ from: "player", text: "Mom, how's everything going?" },
{ from: "character", text: "Oh you know, the usual. Are you eating well? Don't skip meals honey." },
{ from: "player", text: "I'm eating. Promise. You take care of yourself too." },
{ from: "character", text: "I will. You're so good to me. Love you." },
{ from: "player", text: "Love you too mom." }
],
[
{ from: "player", text: "Hey mom, how's your day going?" },
{ from: "character", text: "Pretty good sweetie. Want to come over for dinner tonight? Just us two?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes, I'd love that. What time?", then: [ { from: "character", text: "Six? I'll make your favorite. Can't wait to see you honey." } ] },
{ label: "Not today", playerText: "Maybe another day. I'm busy tonight.", then: [ { from: "character", text: "No problem. Another time. Love you." } ] }
]}
],
[
{ from: "player", text: "Mom, how's everything going?" },
{ from: "character", text: "Oh you know, the usual. Can you help me with groceries this weekend?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure mom. I'll drive.", then: [ { from: "character", text: "Thank you honey. You're the best. Love you." } ] },
{ label: "I can't", playerText: "Sorry, I've got plans. Next time?", then: [ { from: "character", text: "That's okay. Let me know if something changes. Love you." } ] }
]}
]
]
},
{
id: "whatUp",
version: 1,
label: "What are you up to?",
category: "friendship",
tier: 0,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "What are you doing right now?" },
{ from: "character", text: "Just relaxing with some tea. Had a busy morning with all the errands." },
{ from: "player", text: "Sounds nice. I'll be home later." },
{ from: "character", text: "Okay honey. See you then." }
],
[
{ from: "player", text: "Yo mom, what's up?" },
{ from: "character", text: "Watching TV and folding clothes. Nothing exciting! Your father's in the garage again." },
{ from: "player", text: "Cool. I'll see you tonight." },
{ from: "character", text: "Okay sweetie. Don't be late." }
],
[
{ from: "player", text: "What are you up to right now?" },
{ from: "character", text: "Making dinner plans. Will you be home to eat with us tonight?" },
{ from: "player", text: "Yeah, I'll be there. What're we having?" },
{ from: "character", text: "Nothing fancy. Just wanted to see you. Miss you already." },
{ from: "player", text: "Same. See you at dinner." }
],
[
{ from: "player", text: "Mom, you busy?" },
{ from: "character", text: "Watching TV and folding clothes. Nothing exciting! Just the usual mom stuff." },
{ from: "player", text: "Sounds peaceful. I'll leave you to it." },
{ from: "character", text: "You don't have to. I like when you text. Had a busy morning with all the errands." },
{ from: "player", text: "Okay. Love you. Bye." }
],
[
{ from: "player", text: "What are you doing right now?" },
{ from: "character", text: "Just relaxing. Will you be home for dinner tonight? I'm making something nice." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. What time? I'll be there.", then: [ { from: "character", text: "Six. See you then honey. Love you." } ] },
{ label: "No", playerText: "Not tonight. Maybe tomorrow?", then: [ { from: "character", text: "Okay sweetie. Another time. Miss you." } ] }
]}
],
[
{ from: "player", text: "Mom, you busy?" },
{ from: "character", text: "Watching TV. Want to video call later? I miss your face." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. In an hour?", then: [ { from: "character", text: "Perfect. I'll be here. Love you." } ] },
{ label: "Can't", playerText: "Not today. Text you tomorrow?", then: [ { from: "character", text: "Okay. Talk then. Love you." } ] }
]}
]
]
},
{
id: "helpOffer",
version: 1,
label: "Need any help?",
category: "friendship",
tier: 20,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "Do you need help with anything around the house?" },
{ from: "character", text: "That's so sweet of you to offer! Actually, could you help me with the groceries this weekend?" },
{ from: "player", text: "Sure, I can do that." },
{ from: "character", text: "Thank you honey. You're the best." }
],
[
{ from: "player", text: "Need a hand with anything?" },
{ from: "character", text: "Aw honey, I'm okay for now. But it's really nice of you to ask! Maybe later with the pantry – it's a damn mess." },
{ from: "player", text: "Just say when." },
{ from: "character", text: "I will. Love you." }
],
[
{ from: "player", text: "Anything I can help with around the house?" },
{ from: "character", text: "Well, if you're free later, maybe you could help me organize the pantry? It's a mess!" },
{ from: "player", text: "Sure. When do you want to do it?" },
{ from: "character", text: "This weekend? No rush. That's so sweet of you to offer." },
{ from: "player", text: "Done. Text me when." }
],
[
{ from: "player", text: "You need help with anything?" },
{ from: "character", text: "Actually, could you help me with the groceries this weekend? I hate going alone." },
{ from: "player", text: "Of course. I'll drive." },
{ from: "character", text: "Thank you honey. You're the best. Really." },
{ from: "player", text: "Anything for you mom." }
],
[
{ from: "player", text: "Do you need help with anything around the house?" },
{ from: "character", text: "That's so sweet. Could you help me with the pantry Saturday? It's a mess." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. What time?", then: [ { from: "character", text: "Morning? Ten? Thank you honey. You're the best." } ] },
{ label: "Busy", playerText: "I'm busy Saturday. Sunday okay?", then: [ { from: "character", text: "Sunday works. Thanks for offering. Love you." } ] }
]}
],
[
{ from: "player", text: "Need a hand with anything?" },
{ from: "character", text: "Actually yes. Want to run to the store with me tomorrow? I hate going alone." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yeah. I'll drive.", then: [ { from: "character", text: "Perfect. Thank you sweetie. See you then." } ] },
{ label: "No", playerText: "Can't tomorrow. Next time?", then: [ { from: "character", text: "No problem. Let me know when you're free. Love you." } ] }
]}
]
]
},
{
id: "weekendPlans",
version: 1,
label: "Weekend plans?",
category: "friendship",
tier: 20,
timeAdvance: 2,
statGain: { friendship: 2 },
variations: [
[
{ from: "player", text: "Got any plans for the weekend mom?" },
{ from: "character", text: "Just the usual - cleaning, maybe some gardening. Want to help?" },
{ from: "player", text: "Maybe. I'll let you know." },
{ from: "character", text: "Okay sweetie. You're always welcome." }
],
[
{ from: "player", text: "What's up for the weekend?" },
{ from: "character", text: "Your father wants to go to the hardware store. Thrilling, I know! How about you?" },
{ from: "player", text: "Nothing set. Might hang with friends." },
{ from: "character", text: "Have fun. Be safe. Love you." }
],
[
{ from: "player", text: "Any plans for the weekend?" },
{ from: "character", text: "Nothing set in stone. Maybe we could do something together?" },
{ from: "player", text: "I'd like that. What do you want to do?" },
{ from: "character", text: "Quiet weekend. You're welcome to join us for dinner. Or just us two." },
{ from: "player", text: "Let's do just us two. I'll text you." }
],
[
{ from: "player", text: "Weekend plans mom?" },
{ from: "character", text: "Just the usual - cleaning, maybe some gardening. Want to help?" },
{ from: "player", text: "Maybe. Can I let you know tomorrow?" },
{ from: "character", text: "Okay sweetie. You're always welcome. Your father wants to go to the hardware store. Thrilling, I know!" },
{ from: "player", text: "Haha. Okay. Love you." }
],
[
{ from: "player", text: "Got any plans for the weekend mom?" },
{ from: "character", text: "Nothing set. Want to do something together? Just you and me?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "I'd like that. What do you want to do?", then: [ { from: "character", text: "Lunch and a walk? My treat. Love you honey." } ] },
{ label: "Maybe", playerText: "Maybe. I'll let you know Friday?", then: [ { from: "character", text: "Okay. No pressure. Text me. Love you." } ] }
]}
],
[
{ from: "player", text: "What's up for the weekend?" },
{ from: "character", text: "Your father's going to the hardware store. Want to skip that and have coffee with me instead?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. Saturday morning?", then: [ { from: "character", text: "Perfect. Can't wait. See you then." } ] },
{ label: "No", playerText: "I've got plans. Next weekend?", then: [ { from: "character", text: "Sure. Another time. Love you." } ] }
]}
]
]
},
{
id: "deepTalk",
version: 1,
label: "Deep conversation",
category: "friendship",
tier: 40,
timeAdvance: 3,
statGain: { friendship: 3 },
variations: [
[
{ from: "player", text: "Mom, can we talk about something serious sometime?" },
{ from: "character", text: "Of course honey. Anytime. Is everything okay? You can always talk to me about anything." },
{ from: "player", text: "Yeah. I just need to figure some stuff out." },
{ from: "character", text: "I'm here for you. Whenever you're ready." }
],
[
{ from: "player", text: "I need to talk to you about something." },
{ from: "character", text: "I'm always here for you sweetie. What's on your mind? Want to sit down over coffee?" },
{ from: "player", text: "Not right now. Soon though." },
{ from: "character", text: "Whenever. I love you. You know that." }
],
[
{ from: "player", text: "Mom, I need to talk to you about something serious." },
{ from: "character", text: "Of course honey. Anytime. Is everything okay? You can always talk to me about anything." },
{ from: "player", text: "It's not bad. Just... stuff I've been carrying. I need to figure some stuff out." },
{ from: "character", text: "I'm here for you. Whenever you're ready. Want to sit down over coffee?" },
{ from: "player", text: "Yeah. Soon. Thanks mom." }
],
[
{ from: "player", text: "Can we have a real talk sometime?" },
{ from: "character", text: "I'm always here for you sweetie. What's on your mind?" },
{ from: "player", text: "Nothing I can put in a text. Face to face better." },
{ from: "character", text: "Absolutely dear. Let's find some quiet time soon. I'm here for you, always." },
{ from: "player", text: "Love you. I'll come find you." }
],
[
{ from: "player", text: "Mom, can we talk about something serious sometime?" },
{ from: "character", text: "Of course honey. Do you want to talk now or in person? I'm here either way." },
{ type: "choice", options: [
{ label: "In person", playerText: "In person. Can we sit down soon?", then: [ { from: "character", text: "Whenever you're ready. I'm here. Love you." } ] },
{ label: "Later", playerText: "Not yet. Soon. I just wanted you to know.", then: [ { from: "character", text: "I'm here whenever. No rush. Love you." } ] }
]}
],
[
{ from: "player", text: "I need to talk to you about something." },
{ from: "character", text: "I'm listening. Want to meet for coffee so we can talk properly?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. This week?", then: [ { from: "character", text: "Name the day. I'll be there. You can tell me anything." } ] },
{ label: "Not yet", playerText: "I need a bit more time. But thanks.", then: [ { from: "character", text: "Take your time. I'm not going anywhere. Love you." } ] }
]}
]
]
},
{
id: "sharePersonal",
version: 1,
label: "Share something",
category: "friendship",
tier: 40,
timeAdvance: 3,
statGain: { friendship: 3 },
variations: [
[
{ from: "player", text: "I've been thinking about a lot of stuff lately..." },
{ from: "character", text: "I'm listening honey. You know you can share anything with me. What's been on your mind?" },
{ from: "player", text: "I'll tell you when I'm ready. Thanks mom." },
{ from: "character", text: "Anytime. I'm here for you." }
],
[
{ from: "player", text: "There's something I've been meaning to tell you." },
{ from: "character", text: "That sounds deep. Want to talk about it? I've got time for you sweetie." },
{ from: "player", text: "Not yet. Soon." },
{ from: "character", text: "I'm here. Whenever you're ready." }
],
[
{ from: "player", text: "There's something I've been meaning to tell you." },
{ from: "character", text: "I'm listening honey. You know you can share anything with me. What's been on your mind?" },
{ from: "player", text: "It's not easy to say. I'll tell you when I'm ready. Thanks for not pushing." },
{ from: "character", text: "That sounds deep. Want to talk about it? I've got time for you sweetie. Anytime." },
{ from: "player", text: "I know. Love you." }
],
[
{ from: "player", text: "I've been thinking about a lot of stuff lately..." },
{ from: "character", text: "You can always talk to me. What's going on?" },
{ from: "player", text: "Nothing bad. Just... life. You know?" },
{ from: "character", text: "I'm here for you. Tell me when you're ready. No rush." },
{ from: "player", text: "Thanks mom. Means a lot." }
],
[
{ from: "player", text: "I've been thinking about a lot of stuff lately..." },
{ from: "character", text: "I'm here. Do you want to talk about it now or wait until we're face to face?" },
{ type: "choice", options: [
{ label: "Face to face", playerText: "Face to face. Can we find time this week?", then: [ { from: "character", text: "Of course. Anytime. Just say when. Love you." } ] },
{ label: "Not yet", playerText: "Not yet. Just needed to say it's on my mind.", then: [ { from: "character", text: "Okay. I'm here when you're ready. Always." } ] }
]}
],
[
{ from: "player", text: "There's something I've been meaning to tell you." },
{ from: "character", text: "You can tell me anything. Want to come over so we can talk?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. Tomorrow?", then: [ { from: "character", text: "Tomorrow works. I'll be here. Love you honey." } ] },
{ label: "Later", playerText: "I'll tell you when I'm ready. Soon.", then: [ { from: "character", text: "Whenever. I'm not going anywhere. Love you." } ] }
]}
]
]
},
{
id: "lifeAdvice",
version: 1,
label: "Life advice",
category: "friendship",
tier: 60,
timeAdvance: 3,
statGain: { friendship: 4 },
variations: [
[
{ from: "player", text: "Mom, I could really use some advice..." },
{ from: "character", text: "Of course sweetie. I'm always here to help however I can. What do you need?" },
{ from: "player", text: "It's about school and stuff. Can we talk later?" },
{ from: "character", text: "Of course. Whenever you're ready honey." }
],
[
{ from: "player", text: "I need your advice on something." },
{ from: "character", text: "Lay it on me. I'm listening. What's going on?" },
{ from: "player", text: "It's kinda heavy. I'll call you." },
{ from: "character", text: "Okay. I've got your back. You know that." }
],
[
{ from: "player", text: "Mom, I could really use some advice..." },
{ from: "character", text: "Of course sweetie. I'm always here to help however I can. What do you need?" },
{ from: "player", text: "It's about school and stuff. And maybe... relationships. Can we talk later?" },
{ from: "character", text: "Of course. Whenever you're ready honey. I'll help you figure it out. Tell me what's going on." },
{ from: "player", text: "I will. Thanks. Love you." }
],
[
{ from: "player", text: "I need your advice on something." },
{ from: "character", text: "Lay it on me. I'm listening. What's going on?" },
{ from: "player", text: "It's kinda heavy. Not for text. I'll call you or we'll talk at home." },
{ from: "character", text: "Anytime. You know I've got your back. What's the matter?" },
{ from: "player", text: "Just... need my mom. Thanks." }
],
[
{ from: "player", text: "Mom, I could really use some advice..." },
{ from: "character", text: "Of course. Do you want to talk now or can we meet up so you can tell me properly?" },
{ type: "choice", options: [
{ label: "Meet up", playerText: "Meet up. Can we get coffee soon?", then: [ { from: "character", text: "Name the day. I'm here. Love you." } ] },
{ label: "Later", playerText: "I'll call you. Maybe tonight?", then: [ { from: "character", text: "Anytime. I've got your back. Love you." } ] }
]}
],
[
{ from: "player", text: "I need your advice on something." },
{ from: "character", text: "Lay it on me. Want to do it over text or in person? Heavy stuff is easier face to face." },
{ type: "choice", options: [
{ label: "In person", playerText: "In person. This weekend?", then: [ { from: "character", text: "Sure. Come find me. I'm here. Love you." } ] },
{ label: "Text", playerText: "I'll try to explain. It's about...", then: [ { from: "character", text: "Take your time. I'm listening. Always." } ] }
]}
]
]
},
/* === LOVE === */
{
id: "missYou",
version: 1,
label: "I miss you",
category: "love",
tier: 20,
timeAdvance: 3,
statGain: { love: 2, friendship: 1 },
variations: [
[
{ from: "player", text: "I miss you mom" },
{ from: "character", text: "Aw honey, I miss you too! We should spend more quality time together." },
{ from: "player", text: "Yeah. Let's do something soon." },
{ from: "character", text: "I'd love that. Love you ❤️" }
],
[
{ from: "player", text: "Just missing you." },
{ from: "character", text: "I miss you too sweetie. So much. Let's plan something soon, just us. ❤️" },
{ from: "player", text: "Deal." },
{ from: "character", text: "You're the best. Love you." }
],
[
{ from: "player", text: "I miss you mom" },
{ from: "character", text: "Aw honey, I miss you too! We should spend more quality time together." },
{ from: "player", text: "Yeah. Let's do something soon. Just us." },
{ from: "character", text: "I'd love that. Love you ❤️ Let's plan something special." },
{ from: "player", text: "Deal. Miss you." }
],
[
{ from: "player", text: "Just missing you." },
{ from: "character", text: "I miss you too sweetie. So much. You're so sweet. I miss having you around more." },
{ from: "player", text: "Same. I'll come see you soon." },
{ from: "character", text: "Love you honey. You're the best. Text me when you're free." },
{ from: "player", text: "Love you too. Bye." }
],
[
{ from: "player", text: "I miss you mom" },
{ from: "character", text: "I miss you too! Want to plan something soon? Just us two?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. I'd love that. This weekend?", then: [ { from: "character", text: "Perfect. Let's do it. Love you ❤️" } ] },
{ label: "Soon", playerText: "Soon. I'll text you when I'm free.", then: [ { from: "character", text: "Okay. I'll be here. Love you." } ] }
]}
],
[
{ from: "player", text: "Just missing you." },
{ from: "character", text: "Me too. Should we have a movie night at home? You pick the film." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. Friday?", then: [ { from: "character", text: "Friday it is. Can't wait. Love you ❤️" } ] },
{ label: "Another time", playerText: "Maybe next week. Busy this week.", then: [ { from: "character", text: "No problem. Miss you. Love you." } ] }
]}
]
]
},
{
id: "loveYou",
version: 1,
label: "Love you mom",
category: "love",
tier: 20,
timeAdvance: 3,
statGain: { love: 2, friendship: 1 },
variations: [
[
{ from: "player", text: "Just wanted to say I love you" },
{ from: "character", text: "Aw honey, I love you too so much! You always know how to make me smile. ❤️" },
{ from: "player", text: "You're the best." },
{ from: "character", text: "You're going to make me cry! I love you more than you know." }
],
[
{ from: "player", text: "Love you mom." },
{ from: "character", text: "I love you too baby. So much. You're my everything. ❤️" },
{ from: "player", text: "Same." },
{ from: "character", text: "Take care of yourself. Call me anytime." }
],
[
{ from: "player", text: "Just wanted to say I love you" },
{ from: "character", text: "Aw honey, I love you too so much! You always know how to make me smile. ❤️" },
{ from: "player", text: "You're the best. Seriously." },
{ from: "character", text: "You're going to make me cry! I love you more than you know. You're my everything." },
{ from: "player", text: "Love you more. Bye mom." }
],
[
{ from: "player", text: "Love you mom." },
{ from: "character", text: "I love you too baby. So much. You're my everything. ❤️" },
{ from: "player", text: "Same. Just wanted you to know." },
{ from: "character", text: "I know. I feel it too. Call me anytime. Love you." },
{ from: "player", text: "I will. Bye." }
],
[
{ from: "player", text: "Just wanted to say I love you" },
{ from: "character", text: "I love you too! Want to come over so I can hug you in person?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. Tomorrow?", then: [ { from: "character", text: "Perfect. I'll make something nice. Love you ❤️" } ] },
{ label: "Soon", playerText: "Soon. Just wanted you to know.", then: [ { from: "character", text: "I know. Love you too. So much." } ] }
]}
],
[
{ from: "player", text: "Love you mom." },
{ from: "character", text: "Love you too baby. Can I call you tonight? I just want to hear your voice." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. After eight?", then: [ { from: "character", text: "I'll call you. Love you ❤️" } ] },
{ label: "Busy", playerText: "I'm busy tonight. Tomorrow?", then: [ { from: "character", text: "Tomorrow. Love you." } ] }
]}
]
]
},
{
id: "flirt",
version: 1,
label: "Flirt",
category: "love",
tier: 40,
requiredStats: { friendship: 20 },
timeAdvance: 3,
statGain: { love: 3, friendship: 1 },
variations: [
[
{ from: "player", text: "You look really beautiful today mom" },
{ from: "character", text: "Oh honey, you're making me blush! That's very sweet of you to say." },
{ from: "player", text: "I mean it. You really do." },
{ from: "character", text: "Thank you sweetie. You always know how to make me feel good." }
],
[
{ from: "player", text: "You look amazing today." },
{ from: "character", text: "That's such a nice thing to say. Your father never notices anymore... but you do. Thank you." },
{ from: "player", text: "He's an idiot. You're gorgeous." },
{ from: "character", text: "Oh god, you're going to spoil me. I love it. Thank you honey." }
],
[
{ from: "player", text: "You look really beautiful today mom" },
{ from: "character", text: "Oh honey, you're making me blush! That's very sweet of you to say." },
{ from: "player", text: "I mean it. You really do. You always look good." },
{ from: "character", text: "Thank you sweetie. You always know how to make me feel good. Your father never notices anymore... but you do." },
{ from: "player", text: "His loss. You're gorgeous." }
],
[
{ from: "player", text: "You look amazing today." },
{ from: "character", text: "That's such a nice thing to say. Thank you." },
{ from: "player", text: "It's the truth. You're stunning." },
{ from: "character", text: "You're such a charmer! Where did you learn to be so sweet? I love it." },
{ from: "player", text: "From you. Love you." }
],
[
{ from: "player", text: "You look really beautiful today mom" },
{ from: "character", text: "Thank you honey. You're so sweet. Want to get lunch together this week? Just us?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. I'd like that.", then: [ { from: "character", text: "Me too. Name the day. Love you." } ] },
{ label: "Maybe", playerText: "Maybe next week. Schedule's crazy.", then: [ { from: "character", text: "No problem. Whenever. Love you." } ] }
]}
],
[
{ from: "player", text: "You look amazing today." },
{ from: "character", text: "That's so nice. You always know how to make me feel good. Should we take a selfie together next time we see each other?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. I'd like that.", then: [ { from: "character", text: "Perfect. Can't wait to see you. Love you." } ] },
{ label: "Sure", playerText: "Sure. Next time.", then: [ { from: "character", text: "Deal. Love you honey." } ] }
]}
]
]
},
{
id: "compliment",
version: 1,
label: "Compliment",
category: "love",
tier: 40,
timeAdvance: 3,
statGain: { love: 3, friendship: 1 },
variations: [
[
{ from: "player", text: "You're the best mom anyone could ask for" },
{ from: "character", text: "Oh sweetie... that means the world to me. Thank you so much." },
{ from: "player", text: "Just saying the truth." },
{ from: "character", text: "I try my best honey. I love you." }
],
[
{ from: "player", text: "Nobody's mom is as cool as you." },
{ from: "character", text: "Oh god, you're going to make me cry! Thank you. I love you." },
{ from: "player", text: "It's true." },
{ from: "character", text: "You make it easy. Love you too." }
],
[
{ from: "player", text: "You're the best mom anyone could ask for" },
{ from: "character", text: "Oh sweetie... that means the world to me. Thank you so much." },
{ from: "player", text: "Just saying the truth. I'm lucky to have you." },
{ from: "character", text: "I try my best honey. I love you. You're going to make me cry!" },
{ from: "player", text: "Don't cry. Love you. Bye." }
],
[
{ from: "player", text: "Nobody's mom is as cool as you." },
{ from: "character", text: "Oh god, you're going to make me cry! Thank you. I love you." },
{ from: "player", text: "It's true. You're amazing." },
{ from: "character", text: "That's the sweetest thing. I'm lucky to have you too dear. Love you." },
{ from: "player", text: "Love you too mom." }
],
[
{ from: "player", text: "You're the best mom anyone could ask for" },
{ from: "character", text: "Oh sweetie... that means the world. Can I see you this week? I need a hug." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "Whenever you're free. Thank you. Love you." } ] },
{ label: "Soon", playerText: "Soon. I promise. Busy right now.", then: [ { from: "character", text: "I understand. Love you. Come when you can." } ] }
]}
],
[
{ from: "player", text: "Nobody's mom is as cool as you." },
{ from: "character", text: "Thank you. Want to do something fun together? My treat." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. What did you have in mind?", then: [ { from: "character", text: "Lunch and shopping? Or just coffee. You pick. Love you." } ] },
{ label: "Another time", playerText: "I'd love to but not this week.", then: [ { from: "character", text: "Another time then. Love you." } ] }
]}
]
]
},
{
id: "meanEverything",
version: 1,
label: "You mean everything",
category: "love",
tier: 60,
timeAdvance: 5,
statGain: { love: 4, friendship: 2 },
variations: [
[
{ from: "player", text: "Mom, you mean everything to me" },
{ from: "character", text: "Oh honey... you're going to make me cry. I love you so, so much. You're my world too. ❤️" },
{ from: "player", text: "I just wanted you to know." },
{ from: "character", text: "That means more to me than you'll ever know. You're my everything too." }
],
[
{ from: "player", text: "You're my whole world. You know that?" },
{ from: "character", text: "Sweetie... I feel the same. You're my everything. Always have been. ❤️" },
{ from: "player", text: "Good." },
{ from: "character", text: "I love you. So much." }
],
[
{ from: "player", text: "Mom, you mean everything to me" },
{ from: "character", text: "Oh honey... you're going to make me cry. I love you so, so much. You're my world too. ❤️" },
{ from: "player", text: "I just wanted you to know. Every day." },
{ from: "character", text: "That means more to me than you'll ever know. You're my everything too. Always." },
{ from: "player", text: "Good. Same here. Love you." }
],
[
{ from: "player", text: "Mom, you mean everything to me" },
{ from: "character", text: "Oh honey... I feel the same. Can we see each other soon? I need to tell you in person." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. This week?", then: [ { from: "character", text: "Perfect. I love you. So much. ❤️" } ] },
{ label: "Soon", playerText: "Soon. I'll come find you.", then: [ { from: "character", text: "I'll be here. Love you. Always. ❤️" } ] }
]}
],
[
{ from: "player", text: "You're my whole world. You know that?" },
{ from: "character", text: "Sweetie... I feel the same. You're my everything. Always have been. ❤️" },
{ from: "player", text: "Good. Don't ever forget it." },
{ from: "character", text: "I won't. I love you. So much. You're my world too." },
{ from: "player", text: "Love you mom. Bye." }
],
[
{ from: "player", text: "You're my whole world. You know that?" },
{ from: "character", text: "Sweetie... you're my world too. Want to have a quiet dinner together? Just us?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "This weekend? I'll cook. Love you. ❤️" } ] },
{ label: "Soon", playerText: "Soon. I promise.", then: [ { from: "character", text: "I know. Love you. So much. ❤️" } ] }
]}
]
]
},
{
id: "deepFeelings",
version: 1,
label: "Deep feelings",
category: "love",
tier: 60,
timeAdvance: 5,
statGain: { love: 4, friendship: 2 },
variations: [
[
{ from: "player", text: "I don't know what I'd do without you mom" },
{ from: "character", text: "And I don't know what I'd do without you honey. We're a team. Always. ❤️" },
{ from: "player", text: "We've got each other." },
{ from: "character", text: "Always. I'm right here." }
],
[
{ from: "player", text: "Don't ever leave me. Seriously." },
{ from: "character", text: "You'll never have to find out. I'm here for you, no matter what. We've got each other." },
{ from: "player", text: "Thanks mom." },
{ from: "character", text: "Always. Love you." }
],
[
{ from: "player", text: "I don't know what I'd do without you mom" },
{ from: "character", text: "And I don't know what I'd do without you honey. We're a team. Always. ❤️" },
{ from: "player", text: "We've got each other. No matter what." },
{ from: "character", text: "Always. I'm right here. You'll never have to find out. I'm here for you, no matter what." },
{ from: "player", text: "Thanks mom. Love you." }
],
[
{ from: "player", text: "Don't ever leave me. Seriously." },
{ from: "character", text: "You'll never have to find out. I'm here for you, no matter what. We've got each other." },
{ from: "player", text: "Thanks. I needed to hear that." },
{ from: "character", text: "Anytime. Always. Love you. You're my world too." },
{ from: "player", text: "Love you too. Bye." }
],
[
{ from: "player", text: "I don't know what I'd do without you mom" },
{ from: "character", text: "We've got each other. Want to come over so we can just be together for a bit?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. This weekend?", then: [ { from: "character", text: "Perfect. I'll be here. Love you. Always. ❤️" } ] },
{ label: "Soon", playerText: "Soon. I'll come find you.", then: [ { from: "character", text: "I'm not going anywhere. Love you." } ] }
]}
],
[
{ from: "player", text: "Don't ever leave me. Seriously." },
{ from: "character", text: "I won't. Can we have a quiet night in soon? Just you and me?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "You name it. I'm here. Love you." } ] },
{ label: "Later", playerText: "I need some time. But thanks.", then: [ { from: "character", text: "Anytime. I'm here. Love you." } ] }
]}
]
]
},
/* === LUST === */
{
id: "lookGood",
version: 1,
label: "You look good",
category: "lust",
tier: 20,
timeAdvance: 2,
statGain: { lust: 3, love: 1 },
variations: [
[
{ from: "player", text: "You're looking really good today mom" },
{ from: "character", text: "Oh! That's... sweet of you to notice honey. Thank you." },
{ from: "player", text: "Just saying what I see." },
{ from: "character", text: "Well thank you sweetie! I did put in a little extra effort today." }
],
[
{ from: "player", text: "You look great. Like, really great." },
{ from: "character", text: "Your father never notices these things anymore! But you do. Thank you." },
{ from: "player", text: "His loss." },
{ from: "character", text: "That's sweet. Love you honey." }
],
[
{ from: "player", text: "You're looking really good today mom" },
{ from: "character", text: "Oh! That's... sweet of you to notice honey. Thank you." },
{ from: "player", text: "Just saying what I see. You always look good." },
{ from: "character", text: "Well thank you sweetie! I did put in a little extra effort today. Your father never notices!" },
{ from: "player", text: "His loss. You're beautiful." }
],
[
{ from: "player", text: "You look great. Like, really great." },
{ from: "character", text: "Your father never notices these things anymore! But you do. Thank you." },
{ from: "player", text: "I notice. Every time. You're gorgeous." },
{ from: "character", text: "That's sweet. Love you honey. You're so good to me." },
{ from: "player", text: "Love you too. Bye." }
],
[
{ from: "player", text: "You're looking really good today mom" },
{ from: "character", text: "Thank you! Want to go for a walk together this week? I could use the company." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. When?", then: [ { from: "character", text: "Saturday morning? My treat for coffee after. Love you." } ] },
{ label: "Maybe", playerText: "Maybe. I'll let you know.", then: [ { from: "character", text: "No pressure. Love you honey." } ] }
]}
],
[
{ from: "player", text: "You look great. Like, really great." },
{ from: "character", text: "That's sweet. Your father never says that. Should we take a photo together next time? For me?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Sure. Next time we see each other.", then: [ { from: "character", text: "Can't wait. Love you." } ] },
{ label: "Sure", playerText: "Yeah. I'd like that.", then: [ { from: "character", text: "Perfect. Love you." } ] }
]}
]
]
},
{
id: "spicyCompliment",
version: 1,
label: "Suggestive compliment",
category: "lust",
tier: 40,
requiredStats: { love: 20 },
timeAdvance: 3,
statGain: { lust: 4, love: 1 },
variations: [
[
{ from: "player", text: "Mom, you're looking really attractive today" },
{ from: "character", text: "Oh my... honey, you're making me blush! That's quite forward of you." },
{ from: "player", text: "I mean it." },
{ from: "character", text: "That's... very bold sweetie. But thank you. You're flattering me!" }
],
[
{ from: "player", text: "You look hot today. Seriously." },
{ from: "character", text: "Jesus, honey! You can't just say things like that! But... fuck, thank you. I needed to hear that." },
{ from: "player", text: "Your husband's blind. You're gorgeous." },
{ from: "character", text: "Oh god. Stop. You're embarrassing me. But... I love it. Don't tell anyone I said that." }
],
[
{ from: "player", text: "Mom, you're looking really attractive today" },
{ from: "character", text: "Oh my... honey, you're making me blush! That's quite forward of you." },
{ from: "player", text: "I mean it. You're stunning. Seriously." },
{ from: "character", text: "That's... very bold sweetie. But thank you. You're flattering me! You're such a charmer." },
{ from: "player", text: "Just honest. Love you." }
],
[
{ from: "player", text: "You look hot today. Seriously." },
{ from: "character", text: "Jesus, honey! You can't just say things like that! But... fuck, thank you. I needed to hear that." },
{ from: "player", text: "Your husband's blind. You're gorgeous. I can't help it." },
{ from: "character", text: "Oh god. Stop. You're embarrassing me. But... I love it. Don't tell anyone. Thank you." },
{ from: "player", text: "My secret. Bye mom." }
],
[
{ from: "player", text: "Mom, you're looking really attractive today" },
{ from: "character", text: "Oh my... you're so bold. Want to come over tonight? We could have wine. Just us." },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. I'd like that.", then: [ { from: "character", text: "Me too. See you tonight. Don't tell anyone. 💋" } ] },
{ label: "Not tonight", playerText: "Not tonight. Another time?", then: [ { from: "character", text: "Whenever you want. I'll be here. 💋" } ] }
]}
],
[
{ from: "player", text: "You look hot today. Seriously." },
{ from: "character", text: "Jesus, honey... You're making me feel things. Want to send me a picture of you? Something nice?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Maybe. What kind?", then: [ { from: "character", text: "Whatever you're comfortable with. I just want to see you. 💋" } ] },
{ label: "Later", playerText: "Maybe later. You're so hot when you're flustered.", then: [ { from: "character", text: "You're trouble. I love it. Text me. 🔥" } ] }
]}
]
]
},
{
id: "whatWearing",
version: 1,
label: "What are you wearing?",
category: "lust",
tier: 40,
timeAdvance: 3,
statGain: { lust: 4, love: 1 },
variations: [
[
{ from: "player", text: "What are you wearing right now?" },
{ from: "character", text: "Just my usual loungewear. Nothing special! Why do you ask?" },
{ from: "player", text: "Just curious." },
{ from: "character", text: "Curious, aren't you? Just casual stuff honey." }
],
[
{ from: "player", text: "What're you wearing? 😏" },
{ from: "character", text: "Oh, just some comfortable clothes. It's too hot to wear much today. Why? You planning something?" },
{ from: "player", text: "Maybe. Just thinking about you." },
{ from: "character", text: "You're trouble. But I like it." }
],
[
{ from: "player", text: "What are you wearing right now?" },
{ from: "character", text: "Just my usual loungewear. Nothing special! Why do you ask?" },
{ from: "player", text: "Just curious. You always look good." },
{ from: "character", text: "Curious, aren't you? Just casual stuff honey. Nothing exciting! It's too hot to wear much today." },
{ from: "player", text: "Okay. Just wondering. Bye." }
],
[
{ from: "player", text: "What're you wearing? 😏" },
{ from: "character", text: "Oh, just some comfortable clothes. It's too hot to wear much today. Why? You planning something?" },
{ from: "player", text: "Maybe. Just thinking about you." },
{ from: "character", text: "You're trouble. But I like it. Nothing fancy. Why? You planning something?" },
{ from: "player", text: "Maybe. Talk later." }
],
[
{ from: "player", text: "What are you wearing right now?" },
{ from: "character", text: "Something comfortable. Why? You want a picture? 😏" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. Send me one?", then: [ { from: "character", text: "Maybe later. You're so bad. I like it. 💋" } ] },
{ label: "Just curious", playerText: "Just curious. You always look good.", then: [ { from: "character", text: "Thank you. You're sweet. And trouble. 🔥" } ] }
]}
],
[
{ from: "player", text: "What're you wearing? 😏" },
{ from: "character", text: "Not much. It's hot. Want me to send you a pic? You send me one too?" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Deal. You first.", then: [ { from: "character", text: "You're on. Check your phone. 🔥💋" } ] },
{ label: "Just you", playerText: "Just you. I want to see you.", then: [ { from: "character", text: "Okay. Just for you. Don't show anyone. 😳" } ] }
]}
]
]
},
{
id: "askSpicySelfie",
version: 1,
label: "Ask for spicy selfie",
category: "lust",
tier: 60,
requiredStats: { love: 40 },
timeAdvance: 3,
statGain: { lust: 5, love: 2 },
imageType: "receiver",
imagePool: "mother",
variations: [
[
{ from: "player", text: "Mom... can you send me a picture? Something... spicy?" },
{ from: "character", text: "Oh my god... you want THAT kind of picture? Honey, this is so wrong but... fuck it. Here. Don't you dare show anyone! 😳" },
{ from: "player", text: "I won't. Promise." },
{ from: "character", text: "You better delete this after! 💋" }
],
[
{ from: "player", text: "Send me something... sexy? Please?" },
{ from: "character", text: "Holy shit, you're serious? I can't believe I'm doing this... okay, just for you. This stays between us. You like what you see? 😏" },
{ from: "player", text: "Fuck yes. You're incredible." },
{ from: "character", text: "God, I'm so wet just thinking about you looking at this... 🔥" }
],
[
{ from: "player", text: "Mom... can you send me a picture? Something... spicy?" },
{ from: "character", text: "Oh my god... you want THAT kind of picture? Honey, this is so wrong but... fuck it. Here. Don't you dare show anyone! 😳" },
{ from: "player", text: "I won't. Promise. You're so fucking hot." },
{ from: "character", text: "You better delete this after! 💋 Jesus Christ, you're serious? I can't believe I'm doing this..." },
{ from: "player", text: "Thank you. You're incredible. My secret." }
],
[
{ from: "player", text: "Send me something... sexy? Please?" },
{ from: "character", text: "Holy shit, you're serious? I can't believe I'm doing this... okay, just for you. This stays between us. You like what you see? 😏" },
{ from: "player", text: "Fuck yes. You're incredible. I can't stop looking." },
{ from: "character", text: "God, I'm so wet just thinking about you looking at this... 🔥 Don't tell anyone. Ever." },
{ from: "player", text: "Never. You're perfect. Thank you." }
],
[
{ from: "player", text: "Mom... can you send me a picture? Something... spicy?" },
{ from: "character", text: "Oh god... okay. Here. But only if you send me one too. Deal? 😳💋" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Deal. I'll send one. Just for you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "I can't believe we're doing this. Send it. I want to see you. 🔥" } ] },
{ label: "No", playerText: "Just you. I want to see you. Maybe I'll send one later.", then: [ { from: "character", text: "Okay. Your eyes only. Don't show anyone. 💋" } ] }
]}
],
[
{ from: "player", text: "Send me something... sexy? Please?" },
{ from: "character", text: "Holy shit... okay. Here. But I want one from you too. Fair's fair. 😏" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Okay. Sending one. Just for you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "Fuck. I'm so wet thinking about it. Send it. 🔥" } ] },
{ label: "Not now", playerText: "Not right now. Just wanted to see you.", then: [ { from: "character", text: "Okay. Maybe later. This stays between us. 💋" } ] }
]}
]
]
},
{
id: "sendSpicySelfie",
version: 1,
label: "Send spicy selfie",
category: "lust",
tier: 60,
timeAdvance: 2,
statGain: { lust: 5 },
imageType: "sender",
imagePool: "player",
variations: [
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "OH MY FUCKING GOD! Honey... your body is... jesus christ you're absolutely gorgeous. I can't stop staring at this. 😳🔥" },
{ from: "player", text: "You like what you see?" },
{ from: "character", text: "I shouldn't be looking at this but... damn. You're perfect. Send more? 😏" }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "Holy shit sweetie... you're stunning. Like, seriously fucking beautiful. I'm getting so turned on right now. This is so wrong but... fuck. 💋" },
{ from: "player", text: "Wrong feels good sometimes." },
{ from: "character", text: "Don't. You're going to make me do something we'll regret. But... send more. Please. 🔥" }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "OH MY FUCKING GOD! Honey... your body is... jesus christ you're absolutely gorgeous. I can't stop staring at this. 😳🔥" },
{ from: "player", text: "You like what you see? I wanted you to see me." },
{ from: "character", text: "I shouldn't be looking at this but... damn. You're perfect. I'm getting so turned on. This is so wrong but... fuck." },
{ from: "player", text: "Send more? Maybe. You're so hot when you're flustered." }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "Holy shit sweetie... you're stunning. Like, seriously fucking beautiful. I'm getting so turned on right now. This is so wrong but... fuck. 💋" },
{ from: "player", text: "Wrong feels good sometimes. You're gorgeous too. I thought about you." },
{ from: "character", text: "Don't. You're going to make me do something we'll regret. But... send more. Please. 🔥 I can't stop looking." },
{ from: "player", text: "Maybe later. You're incredible. Bye." }
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "OH MY GOD. You're so fucking beautiful. Send me another? I'll send you one back. 🔥" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Okay. One more. You send one too.", requiresSpicyPhoto: true, then: [ { from: "character", text: "Deal. Check your phone. Fuck you're hot. 💋🔥" } ] },
{ label: "Later", playerText: "Maybe later. Enjoy that one for now.", then: [ { from: "character", text: "I will. You're incredible. Don't forget. Send more when you want. 😏" } ] }
]}
],
[
{ from: "player", text: "[You send a nude photo]" },
{ from: "character", text: "Jesus Christ... I can't stop looking. Can I send you one back? I want you to see me too. 💋" },
{ type: "choice", options: [
{ label: "Yes", playerText: "Yes. Send it. I want to see you.", requiresSpicyPhoto: true, then: [ { from: "character", text: "Here. Just for you. Your eyes only. 🔥" } ] },
{ label: "Not now", playerText: "Not right now. But thank you. You're so hot.", then: [ { from: "character", text: "Okay. Another time. I'll save it for you. 💋" } ] }
]}
]
]
}
]>>/* Work-related phone topics (Phase 1: Ruby / Vince) */
<<if !setup.phoneMessageTopics>><<set setup.phoneMessageTopics = {}>><</if>>
<<set setup.phoneMessageTopics.dinerManager = [
{
id: "work_excuse_today",
version: 1,
label: "Can't come to shift today",
customRules: {
jobExcuseBeforeNoon: true
},
category: "friendship",
tier: 0,
timeAdvance: 1,
statGain: {},
variations: [
[
{ from: "player", text: "Hey Vince, I need to let you know I can't make today's shift." },
{ from: "character", text: "You better have a good reason. What's going on?" },
{ type: "choice", options: [
{ label: "Tell the truth", playerText: "I'm being honest with you. I can't make it today.", customAction: { type: "job_excuse", mode: "honest" }, thenByMode: {
honest: [
{ from: "character", text: "Noted. Next time give me a little more notice." }
]
}},
{ label: "Lie: I'm sick", playerText: "I'm really sick today. I can't stand up straight.", requiresCorruptionEq: 2, customAction: { type: "job_excuse", mode: "lie", lieType: "sick" }, thenByMode: {
lie_success: [
{ from: "character", text: "Fine. Rest up and be ready tomorrow." }
],
lie_fail: [
{ from: "character", text: "That doesn't sound right. We'll talk at work." }
]
}},
{ label: "Lie: Family emergency", playerText: "Family emergency came up. I need to handle it right now.", requiresCorruptionEq: 2, customAction: { type: "job_excuse", mode: "lie", lieType: "family" }, thenByMode: {
lie_success: [
{ from: "character", text: "Alright. Handle it and update me tomorrow." }
],
lie_fail: [
{ from: "character", text: "This sounds like an excuse. We'll discuss this in person." }
]
}},
{ label: "Lie: Transport issue", playerText: "I'm stuck with a transport problem and can't get there on time.", requiresCorruptionEq: 2, customAction: { type: "job_excuse", mode: "lie", lieType: "transport" }, thenByMode: {
lie_success: [
{ from: "character", text: "Understood. Be here on time for your next shift." }
],
lie_fail: [
{ from: "character", text: "I don't buy that. We'll have a proper talk at work." }
]
}}
]}
]
]
}
]>>/* ==========================================
QUEST SYSTEM VARIABLES
Stores active and completed quests.
Structure based on assets/system/js/modal/journal.js expectation:
$quests = {
active: [],
completed: []
}
Quest Object Structure:
{
id: "questId",
title: "Quest Title",
desc: "Quest Description",
objectives: [
{ text: "Objective 1", completed: false }
]
}
========================================== */
<<set $quests = {
active: [],
completed: []
}>>
/* Event Log / Discovery Log */
<<set $gameLog = []>>
/* Certificates / Achievements */
<<set $certificates = []>>/* ==========================================
GAME SETTINGS
Simulation toggles for optional features
========================================== */
<<set $gameSettings = {
/* Basic Needs */
trackHunger: true,
trackThirst: true,
trackBladder: true,
trackCalories: true,
hygieneRequirement: true,
/* Appearance Changes */
hairGrowth: true,
hairMessiness: true,
bodyHairGrowth: true,
makeupWearOff: true,
bodyDegradation: true,
/* Appearance Care Decay (hair/face/dental care drops without routine) */
hairCareDecay: true,
faceCareDecay: true,
dentalCareDecay: true,
/* Decay Systems */
skillDecay: true,
relationshipDecay: true,
/* Arousal ambient systems */
arousalAmbient: true,
/* Talk "Back" target: true = main passage, false = interaction menu */
talkBackToMainPassage: false
}>>
/* ==========================================
VIDEO SETTINGS
Initialize video settings in State.variables
========================================== */
<<script>>
State.variables.videoSettings = State.variables.videoSettings || {
autoplaySet: true,
loopSet: true,
masterVolume: 100,
videoVolume: 100,
navCardAnimations: true,
navCardLayout: 'horizontal'
};
<</script>>
/* ==========================================
CONTENT PREFERENCES
Controls which content types are visible (14 categories)
========================================== */
<<set $contentPreferences = {
maleSexual: false,
femaleSexual: false,
futaTrans: false,
ntr: false,
pregnancy: false,
incest: false,
bdsm: false,
nonConsensual: false,
publicExhibition: false,
lactation: false,
feet: false,
watersports: false,
scat: false,
goreViolence: false,
ageplay: false
}>><<set $timeSys to {
year: 2025,
month: 1,
day: 1,
hour: 8,
minute: 0,
weekday: 1
}>>
<<set $timeConfig to {
monthNames: [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
],
monthDays: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
weekdayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
periods: {
morning: { start: 6, end: 12, name: "Morning" },
afternoon: { start: 12, end: 18, name: "Afternoon" },
evening: { start: 18, end: 22, name: "Evening" },
night: { start: 22, end: 6, name: "Night" }
}
}>>
/* Backward compatibility */
<<set $timeSysYear to $timeSys.year>>/* ==========================================
ACTIVITY BUTTON WIDGET
Unified system for activity buttons with requirement checks
Supports: outfit, inventory, character presence, stat checks
========================================== */
<<widget "activityButton">><<nobr>>
<<silently>>
/* Parse arguments - now using string format for requirements */
<<set _btnText = _args[0]>> /* Button display text */
<<set _targetPassage = _args[1]>> /* Where to go when clicked */
<<set _requirementsStr = _args[2]>> /* String format: "outfit:sporty 3" or "outfit:sporty 3|item:yoga_mat" */
<<set _dailyCheck = _args[3]>> /* Optional: true/false - when true, show locked "Already done today" like Talk */
/* Already done today: show locked button (same as Talk), do not hide */
<<if _dailyCheck === true>>
<<set _canDoActivity = false>>
<<set _tooltipText = "Already done today">>
<<else>>
/* Initialize check result */
<<set _canDoActivity = true>>
<<set _errorMessages = []>>
/* Parse requirements string */
<<if _requirementsStr && _requirementsStr !== "">>
<<set _reqParts = _requirementsStr.split("|")>>
<<for _i = 0; _i < _reqParts.length; _i++>>
<<set _req = _reqParts[_i].trim()>>
<<set _reqType = _req.split(":")[0]>>
<<set _reqValue = _req.split(":")[1]>>
/* Outfit requirement */
<<if _reqType === "outfit">>
<<set _outfitParts = _reqValue.split(" ")>>
<<set _outfitStyle = _outfitParts[0]>>
<<set _outfitCount = parseInt(_outfitParts[1] || 2)>>
<<checkOutfitStyle _outfitStyle _outfitCount 0>>
<<if !State.temporary.outfitCheckResult.allowed>>
<<set _canDoActivity = false>>
<<run _errorMessages.push(State.temporary.outfitCheckResult.reason)>>
<</if>>
<</if>>
/* Item requirement */
<<if _reqType === "item">>
<<checkInventoryItem _reqValue>>
<<if !State.temporary.inventoryCheckResult.allowed>>
<<set _canDoActivity = false>>
<<run _errorMessages.push(State.temporary.inventoryCheckResult.reason)>>
<</if>>
<</if>>
/* Character requirement */
<<if _reqType === "character">>
<<if $characters && $characters[_reqValue]>>
<<set _charPresent = ($characters[_reqValue].currentLocation === $location)>>
<<if !_charPresent>>
<<set _canDoActivity = false>>
<<set _charName = $characters[_reqValue].name || _reqValue>>
<<run _errorMessages.push(_charName + " is not here")>>
<</if>>
<</if>>
<</if>>
/* Min stat requirement */
<<if _reqType === "minStat">>
<<set _statParts = _reqValue.split(" ")>>
<<set _statName = _statParts[0]>>
<<set _minValue = parseInt(_statParts[1])>>
<<set _currentValue = State.variables[_statName] || 0>>
<<if _currentValue < _minValue>>
<<set _canDoActivity = false>>
<<run _errorMessages.push("Need " + _minValue + " " + _statName)>>
<</if>>
<</if>>
/* Max stat requirement */
<<if _reqType === "maxStat">>
<<set _statParts = _reqValue.split(" ")>>
<<set _statName = _statParts[0]>>
<<set _maxValue = parseInt(_statParts[1])>>
<<set _currentValue = State.variables[_statName] || 0>>
<<if _currentValue > _maxValue>>
<<set _canDoActivity = false>>
<<run _errorMessages.push("Need " + _statName + " <= " + _maxValue + " (current: " + _currentValue + ")")>>
<</if>>
<</if>>
<</for>>
<</if>>
/* Combine error messages for tooltip */
<<if _errorMessages.length > 0>>
<<set _tooltipText = _errorMessages.join(" ")>>
<<else>>
<<set _tooltipText = "">>
<</if>>
<</if>>
<</silently>>
<<if _canDoActivity>>
<<btn _btnText _targetPassage "default">><</btn>>
<<else>>
<<set _lockedSpan = '<span class="link-internal btn-style btn-default locked" data-tooltip="' + _tooltipText + '"><i class="icon icon-lock icon-12"></i> ' + _btnText + '</span>'>>
<<print _lockedSpan>>
<</if>>
<</nobr>><</widget>>
/* ==========================================
EXAMPLE USAGE:
Requirements Format: "type:value|type2:value2"
Park Yoga (outfit + item + min energy 25):
<<activityButton "Do Yoga" "parkYoga" "outfit:sporty 3|item:yoga_mat|minStat:energy 25" $daily.yogaDone>>
Park Jog (outfit + min energy 25):
<<activityButton "Go for a Jog" "parkJog" "outfit:sporty 3|minStat:energy 25" $daily.jogDone>>
Nap (with max energy check):
<<activityButton "Take a Nap" "runNap" "maxStat:energy 50" false>>
Yoga with Mom (outfit + item + min energy 25):
<<activityButton "Yoga" "runYoga" "outfit:sporty 2|item:yoga_mat|minStat:energy 25" $daily.yogaDone>>
Character presence check (optional):
<<activityButton "Study with Sarah" "studyWithSarah" "character:sarah|minStat:intelligence 20" false>>
Dance (min energy 20):
<<activityButton "Dance" "runDance" "minStat:energy 20" $daily.danceDone>>
========================================== */
/* ==========================================
ACTIVITY WIDGETS
Daily activity tracking for character interactions (Talk daily limit).
Location activity buttons: single definition per activity, included in passages.
========================================== */
/* ==========================================
DAILY ACTIVITY TRACKING
For character interaction limits
========================================== */
<<widget "logDailyActivity">>
<<nobr>>
<<silently>>
<<set _charId = _args[0]>>
<<set _actionId = _args[1]>>
<<if !_charId || !_actionId>>
/* invalid args */
<<else>>
/* Initialize dailyActivityLog if not exists */
<<if !$dailyActivityLog>>
<<set $dailyActivityLog = {}>>
<</if>>
/* Create activity key with current calendar date (so it resets each real day) */
<<set _dateKey = $timeSys.year + "-" + $timeSys.month + "-" + $timeSys.day>>
<<set _activityKey = _charId + "_" + _actionId + "_" + _dateKey>>
/* Log the activity */
<<set $dailyActivityLog[_activityKey] = true>>
/* Global talk skill gain: all talk interactions grant conversation progress (0.5..1.5, step 0.25) */
<<if _actionId === "talk">>
<<set _convGain = 0.5 + (random(0, 4) * 0.25)>>
<<gainSkill "social" "conversation" _convGain>>
<</if>>
<</if>>
<</silently>>
<</nobr>>
<</widget>>
<<widget "checkDailyActivity">>
<<nobr>>
<<silently>>
<<set _charId = _args[0]>>
<<set _actionId = _args[1]>>
<<if !_charId || !_actionId>>
<<set _hasActivity = false>>
<<else>>
<<if !$dailyActivityLog>>
<<set $dailyActivityLog = {}>>
<</if>>
<<set _dateKey = $timeSys.year + "-" + $timeSys.month + "-" + $timeSys.day>>
<<set _activityKey = _charId + "_" + _actionId + "_" + _dateKey>>
<<set _hasActivity = !!$dailyActivityLog[_activityKey]>>
<</if>>
<</silently>>
<</nobr>>
<</widget>>
/* ==========================================
LOCATION ACTIVITY BUTTONS
One definition per activity – include in passages to avoid duplication.
========================================== */
<<widget "yogaActions">><<nobr>>
<<if $location === "fhBackyard">>
<<set $activityOrigin = "fhBackyard">>
<<activityButton "Yoga" "runYogaSolo" "outfit:sporty 2|item:yoga_mat|minStat:energy 25" $daily.yogaDone>>
<<else>>
<<set $activityOrigin = "fhLivingroom">>
<<set _momPresent = ($characters.mother.currentLocation === "fhLivingroom")>>
<<if _momPresent>>
<<activityButton "Yoga" "runYoga" "outfit:sporty 2|item:yoga_mat|minStat:energy 25" $daily.yogaDone>>
<<else>>
<<activityButton "Yoga" "runYogaSolo" "outfit:sporty 2|item:yoga_mat|minStat:energy 25" $daily.yogaDone>>
<</if>>
<</if>>
<</nobr>><</widget>>
<<widget "danceActions">><<nobr>>
<<set $activityOrigin = "fhLivingroom">>
<<activityButton "Dance" "runDance" "minStat:energy 20" $daily.danceDone>>
<</nobr>><</widget>>
/* ==================================================
BATHROOM WIDGETS
Widgets for bathroom interactions and shower checking
================================================== */
/* Use Toilet + Wash Face buttons (global)
Usage: <<bathroomActions>> – outputs both buttons. Caller must set $returnAfterToilet before calling.
Use inside: <div class="location-actions"><<bathroomActions>></div>
Or combine with other buttons: <<btn "Shower" ...>><<bathroomActions>><<mirrorBtn>> */
<<widget "bathroomActions">>
<<btn "Use Toilet" "useToilet">><</btn>>
<<btn "Wash Face" "washFace">><</btn>>
<</widget>>
/* Bathroom Entry Check Widget
Usage: <<bathroomEntry "fhUpperBath" "Upstairs Bathroom" "fhUpperstairs">>
Args:
- _args[0]: bathroom location ID
- _args[1]: bathroom display name
- _args[2]: return location if turned back
Checks if someone is showering and displays appropriate content:
- If showering: short notice + "Approach the door" (shower encounter) or "Go Back"
- If no one showering: Continue with passage content (caller handles this)
*/
<<widget "bathroomEntry">>
<<nobr>>
<<set _bathroomLoc = _args[0]>>
<<set _bathroomName = _args[1]>>
<<set _returnLoc = _args[2]>>
<<set _someoneShowering = false>>
<<set _showeringChar = null>>
/* Check all characters to see if anyone is showering at this location */
<<for _charId, _char range $characters>>
<<if _charId !== "player" && _char.currentLocation === _bathroomLoc && _char.currentStatus === "showering">>
<<set _someoneShowering = true>>
<<set _showeringChar = _charId>>
<<break>>
<</if>>
<</for>>
/* Store result in State.temporary for passage to use */
<<set State.temporary.bathroomCheck = {
someoneShowering: _someoneShowering,
character: _showeringChar
}>>
<<if _someoneShowering>>
<<narrative _bathroomName>>
The shower is running. <<print $characters[_showeringChar].firstName>> is in there.
<</narrative>>
<div class="location-actions">
<<capture _showeringChar _returnLoc>>
<<btn "Approach the door">>
<<set $showerEncounterTarget = _showeringChar>>
<<set $showerEncounterReturnLoc = _returnLoc>>
<<goto "showerEncounter">>
<</btn>>
<</capture>>
<<capture _returnLoc>>
<<btn "Go Back" _returnLoc "secondary">>
<<unset $showerEncounterTarget>>
<<unset $showerEncounterReturnLoc>>
<</btn>>
<</capture>>
</div>
<</if>>
<</nobr>>
<</widget>>/* ==========================================
BODY SYSTEM
Weight, BMI, Body Type Calculations
========================================== */
/* RECALCULATE BODY TYPE (BMI Based) */
/* Only runs if Body Changes setting is enabled */
<<widget "recalculateBodyType">>
<<if $gameSettings.bodyDegradation>>
/* Calculate BMI = weight / (height in meters)² */
<<set _heightM = $body.height / 100>>
<<set $body.bmi = Math.round(($body.weight / (_heightM * _heightM)) * 10) / 10>>
/* Determine body type based on BMI and muscle mass */
<<if $body.bmi < 18.5>>
<<set $body.bodyType = "Slim">>
<<elseif $body.bmi < 22>>
<<if $body.muscleMass > 45>>
<<set $body.bodyType = "Athletic">>
<<else>>
<<set $body.bodyType = "Normal">>
<</if>>
<<elseif $body.bmi < 25>>
<<if $body.muscleMass > 45>>
<<set $body.bodyType = "Athletic">>
<<elseif ($body.bust > 95 || $body.hips > 100) && $body.waist < 75>>
<<set $body.bodyType = "Curvy">>
<<else>>
<<set $body.bodyType = "Normal">>
<</if>>
<<elseif $body.bmi < 30>>
<<if $body.muscleMass > 50>>
<<set $body.bodyType = "Athletic">>
<<elseif ($body.bust > 95 || $body.hips > 100)>>
<<set $body.bodyType = "Curvy">>
<<else>>
<<set $body.bodyType = "Thick">>
<</if>>
<<else>>
<<set $body.bodyType = "Chubby">>
<</if>>
<</if>>
<</widget>>
/* UPDATE WEIGHT (Daily) */
/* Called from <<advanceDay>> */
<<widget "updateWeight">>
<<if !$gameSettings.trackCalories>>
<<return>>
<</if>>
/* Initialize basalMetabolicRate if undefined (backward compatibility) */
<<if !$basalMetabolicRate>>
<<set $basalMetabolicRate = 2000>>
<</if>>
/* Calculate net calorie balance */
<<set _netCalories = $dailyCalorieIntake - $basalMetabolicRate>>
/* Weight change: 2000 net calories = 0.3kg */
<<if _netCalories >= 2000>>
/* Weight gain - all fat */
<<set _weightGain = Math.floor(_netCalories / 2000) * 0.3>>
<<set $body.weight += _weightGain>>
/* Increase body fat percentage */
<<if $gameSettings.bodyDegradation>>
<<set $body.bodyFat = Math.min(50, $body.bodyFat + (_weightGain * 0.5))>>
<</if>>
<<elseif _netCalories <= -2000>>
/* Weight loss - 70% fat, 30% muscle */
<<set _weightLoss = Math.floor(Math.abs(_netCalories) / 2000) * 0.3>>
<<set $body.weight = Math.max(40, $body.weight - _weightLoss)>>
/* Decrease body fat and muscle mass */
<<if $gameSettings.bodyDegradation>>
<<set $body.bodyFat = Math.max(5, $body.bodyFat - (_weightLoss * 0.7))>>
<<set $body.muscleMass = Math.max(20, $body.muscleMass - (_weightLoss * 0.3))>>
<</if>>
<</if>>
/* Recalculate body type */
<<recalculateBodyType>>
<</widget>>
/* ==========================================
BROTHER TALK TOPICS SCHOOL PHASE, LEVEL 1
phase === "school" && friendshipLevel === 1
Structure: setup.brotherTopics.school.level1[locTimeKey][topicName] = [ {text, friendship, trust, love} ]
CHARACTER: Jake Taylor, 17, high school student
→ Perpetually in a hoodie, headphones around neck
→ Monosyllabic by default, defensive about everything
→ Gaming is religion, school is survival
→ Secretly wants connection, absolutely cannot show it
→ New house, new school = extra stress he won't talk about
SCHOOL L1 TONE:
→ Guarded, minimal, slightly annoyed to be interrupted
→ Sarcastic but not mean it's just how he talks
→ Rare genuine moments feel like cracks in a wall
→ Stat gains: friendship +1-2, trust +1
6 CONTEXTS:
fhKitchen_school_morning fhKitchen_school_evening
fhBrotherRoom_school_afternoon fhBrotherRoom_school_evening
fhLivingroom_school_afternoon fhLivingroom_school_evening
========================================== */
<<set setup.brotherTopics = setup.brotherTopics || {}>>
<<set setup.brotherTopics.school = setup.brotherTopics.school || {}>>
<<set setup.brotherTopics.school.level1 = {
fhKitchen_school_morning: {
sluggish: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's at the counter eating cereal standing up, staring at nothing. Headphones on, one ear free.<</narrative>>
<<dialog "player">>Morning.<</dialog>>
<<dialog "brother">>(doesn't look up) Mm.<</dialog>>
<<dialog "player">>Did you sleep?<</dialog>>
<<dialog "brother">>Some.
(spooning cereal)
I'm fine.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's looking in the fridge blankly. Nothing appeals to him at 7am.<</narrative>>
<<dialog "brother">>(still in the fridge) Did someone eat my yoghurt?<</dialog>>
<<dialog "player">>I think Mom had it.<</dialog>>
<<dialog "brother">>(closing the fridge, resigned) Cool. Great. Perfect.<</dialog>>
<<narrative>>He grabs a piece of bread instead. This is apparently fine.<</narrative>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's almost out the door when you catch him. Bag half-zip, toast in hand.<</narrative>>
<<dialog "player">>Hey did you finish that thing for school?<</dialog>>
<<dialog "brother">>(not stopping) ...Mostly.<</dialog>>
<<dialog "player">>Jake.<</dialog>>
<<dialog "brother">>(already in the hall) It's fine!<</dialog>>`,
friendship: 1
}
],
schoolDread: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's sitting at the table not eating, looking at his phone with the energy of someone awaiting sentencing.<</narrative>>
<<dialog "player">>You okay?<</dialog>>
<<dialog "brother">>(putting the phone face-down) Yeah. Why.<</dialog>>
<<dialog "player">>You look like you'd rather be anywhere else.<</dialog>>
<<dialog "brother">>(getting up) School exists. I have to go to it. It's fine.
(picking up his bag)
Bye.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's checking his backpack for the fourth time. Classic pre-school anxiety tell.<</narrative>>
<<dialog "player">>Forget something?<</dialog>>
<<dialog "brother">>(still checking) No. Maybe. I don't know.<</dialog>>
<<dialog "player">>What do you need?<</dialog>>
<<dialog "brother">>(finding it) Nothing. Got it. Never mind.
(zipping up, relieved)
Forget it.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's at the window watching the bus stop with an expression that's hard to read.<</narrative>>
<<dialog "player">>New school still weird?<</dialog>>
<<dialog "brother">>(not turning) It's not weird. It's just... different.
(a pause)
Everyone already knows each other.<</dialog>>
<<dialog "player">>That gets easier.<</dialog>>
<<dialog "brother">>(collecting his bag) Yeah, whatever.
(heading out, deflecting)
You sound like Mom.<</dialog>>`,
friendship: 2, trust: 1
}
],
grumpy: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He walks past you without a word, pours the last of the orange juice, and puts the empty carton back.<</narrative>>
<<dialog "player">>Jake. The carton.<</dialog>>
<<dialog "brother">>(already leaving) I'll recycle it later.<</dialog>>
<<dialog "player">>You put it back empty.<</dialog>>
<<dialog "brother">>(from the hallway) Yeah, I'll sort it later!<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He comes in, gets water, stands there with it.<</narrative>>
<<dialog "player">>You're going to be late.<</dialog>>
<<dialog "brother">>(drinking) I have eight minutes.<</dialog>>
<<dialog "player">>It's a twenty-minute walk.<</dialog>>
<<dialog "brother">>(pause, doing the maths)
...I walk fast.<</dialog>>
<<narrative>>He leaves quickly, not admitting you were right.<</narrative>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's eating standing up, hoodie half-on, scrolling his phone. The picture of a teenager in transit.<</narrative>>
<<dialog "player">>You should sit down and actually eat.<</dialog>>
<<dialog "brother">>(still scrolling) I eat standing up. It's faster.<</dialog>>
<<dialog "player">>That's not how digestion works.<</dialog>>
<<dialog "brother">>(looks at you for the first time) I'll survive.
(eating his last spoonful, putting the bowl in the sink)
Bye.<</dialog>>`,
friendship: 1
}
]
},
fhKitchen_school_evening: {
schoolDebrief: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's loading his plate, eyes down. School is over but he hasn't fully come back yet.<</narrative>>
<<dialog "player">>How was school?<</dialog>>
<<dialog "brother">>Fine.<</dialog>>
<<dialog "player">>Anything happen?<</dialog>>
<<dialog "brother">>Not really.
(sitting)
It was school. It happened. That's all I've got.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He drops his bag in the kitchen doorway and pours himself a huge glass of water.<</narrative>>
<<dialog "brother">>(drinking) I hate PE.<</dialog>>
<<dialog "player">>That bad?<</dialog>>
<<dialog "brother">>(refilling the glass) We had to do relay races. I tripped. It was fine. It sucked.<</dialog>>
<<dialog "player">>Did anyone say anything?<</dialog>>
<<dialog "brother">>(a beat) No.
(drinking)
Just... annoying.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He comes to dinner and actually sits. No phone out, which is unusual.<</narrative>>
<<dialog "player">>You're not on your phone.<</dialog>>
<<dialog "brother">>(eating) Had to leave it in my bag all day. New rule.
(flatly)
I'm detoxing.<</dialog>>
<<dialog "player">>How's that going?<</dialog>>
<<dialog "brother">>Genuinely terrible.
(takes a bread roll)
This food's good though.<</dialog>>`,
friendship: 2
}
],
food: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's on his second plate before anyone else has finished their first.<</narrative>>
<<dialog "player">>Slow down.<</dialog>>
<<dialog "brother">>(not slowing down) I didn't eat lunch.<</dialog>>
<<dialog "player">>Why not?<</dialog>>
<<dialog "brother">>(vague) The lunch thing at school is kind of a whole situation.
(still eating)
I'll explain later. Maybe.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He opens the fridge and stands in it for thirty seconds before closing it.<</narrative>>
<<dialog "brother">>(closing the fridge) Is there any of that pasta left?<</dialog>>
<<dialog "player">>No, we finished it.<</dialog>>
<<dialog "brother">>(deflating) Okay but that was the only thing I wanted. That specifically.<</dialog>>
<<dialog "player">>I can make something else.<</dialog>>
<<dialog "brother">>(perking up slightly) Like what.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's making toast at 8pm. His personal second dinner.<</narrative>>
<<dialog "player">>Didn't you eat like two hours ago?<</dialog>>
<<dialog "brother">>(watching the toaster) I'm a growing person. Biologically.
(toast pops)
This is science.<</dialog>>`,
friendship: 2
}
],
avoiding: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's in the kitchen but clearly just came to avoid something. Not hungry, just here.<</narrative>>
<<dialog "player">>You okay?<</dialog>>
<<dialog "brother">>(getting water) Yeah.<</dialog>>
<<dialog "player">>You're in the kitchen.<</dialog>>
<<dialog "brother">>(a pause) I know where I am.
(drinks the water)
It's fine. Don't ask.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He sits across from you, uncharacteristically, and doesn't explain why.<</narrative>>
<<dialog "player">>(glancing up) Hey.<</dialog>>
<<dialog "brother">>Hey.
(silence)
What are you doing?<</dialog>>
<<dialog "player">>Just this. You?<</dialog>>
<<dialog "brother">>(shrugging) Nothing. Just. Sitting here I guess.
(doesn't leave for a while)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He comes in, looks like he's about to say something, gets water instead.<</narrative>>
<<dialog "player">>Jake.<</dialog>>
<<dialog "brother">>What.<</dialog>>
<<dialog "player">>Nothing. I'm just saying your name.<</dialog>>
<<dialog "brother">>(a beat) That's weird.
(takes his water and goes, but... a little lighter than before)<</dialog>>`,
friendship: 2
}
]
},
fhBrotherRoom_school_afternoon: {
gaming: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's already at his desk, headset half-on, the game already loading. Zero transition time from school to gaming.<</narrative>>
<<dialog "player">>(at his door) Hey.<</dialog>>
<<dialog "brother">>(not looking) What.<</dialog>>
<<dialog "player">>Nothing, just saying hey.<</dialog>>
<<dialog "brother">>...Hey.
(putting the headset on fully)<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's mid-game. Something intense, judging by his posture. He notices you in the doorway but doesn't stop.<</narrative>>
<<dialog "brother">>(to the screen) One second. One second.
(dies)
Okay. What.<</dialog>>
<<dialog "player">>Do you want anything? I'm making tea.<</dialog>>
<<dialog "brother">>(resuming the queue) Yeah. Whatever you put in it.
(beat)
Thanks.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's loading into a lobby, waiting. A rare still moment.<</narrative>>
<<dialog "brother">>(looking at the loading screen) This game's been broken for two weeks and they haven't fixed it.<</dialog>>
<<dialog "player">>Is that the one you were telling Dad about?<</dialog>>
<<dialog "brother">>(surprised) You remembered that?<</dialog>>
<<dialog "player">>Sort of.<</dialog>>
<<dialog "brother">>(back to his screen, slightly warmer) Yeah. That one.<</dialog>>`,
friendship: 2
}
],
decompressing: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's lying on his bed face-up, not his phone, not gaming. Just staring at the ceiling.<</narrative>>
<<dialog "player">>(from the door) You alive?<</dialog>>
<<dialog "brother">>Barely.
(still looking at ceiling)
School is a lot.<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "brother">>(a moment)
Yeah.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's at his desk but not doing anything. Phone face-down, monitor off.<</narrative>>
<<dialog "player">>Not gaming?<</dialog>>
<<dialog "brother">>(flatly) Taking a break from screens. My eyes hurt.<</dialog>>
<<dialog "player">>Rare.<</dialog>>
<<dialog "brother">>(turning his chair slightly) Shut up.
(a pause, not hostile, just banter)
What do people do without screens?<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's reading. Actual reading. A graphic novel. He closes it when you come in.<</narrative>>
<<dialog "player">>What was that?<</dialog>>
<<dialog "brother">>(sliding it under something) Nothing. A book.<</dialog>>
<<dialog "player">>You read?<</dialog>>
<<dialog "brother">>(defensive) Obviously I read. I'm not illiterate.
(pause)
It's a graphic novel. It doesn't count as reading-reading.<</dialog>>`,
friendship: 2
}
],
homework: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's at his desk with textbooks open. He looks miserable about it.<</narrative>>
<<dialog "player">>Homework?<</dialog>>
<<dialog "brother">>(flatly) What gave it away.<</dialog>>
<<dialog "player">>How much?<</dialog>>
<<dialog "brother">>(gesturing at three different books) All of it. They gave us all of it today.
(staring at the textbook)
I hate Mr. Henley.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's been staring at his screen for a while. Homework tab open. Also three other tabs.<</narrative>>
<<dialog "brother">>(noticing you noticing) I'm doing it. I'm multitasking.<</dialog>>
<<dialog "player">>That's not multitasking, that's avoiding.<</dialog>>
<<dialog "brother">>(brief pause) ...Both.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He spins his chair round and looks at you directly. Unusual.<</narrative>>
<<dialog "brother">>What do you know about photosynthesis?<</dialog>>
<<dialog "player">>...Some things. Why?<</dialog>>
<<dialog "brother">>(pushing his textbook across slightly) I've been looking at this for forty minutes and I don't understand a single word.
(almost admitting defeat)
Can you explain it like I'm five?<</dialog>>`,
friendship: 2, trust: 1
}
]
},
fhBrotherRoom_school_evening: {
lateGaming: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>His room glows blue in the dark. He's fully in the zone. Headset on, mic active, talking quietly to someone online.<</narrative>>
<<dialog "player">>(knocking) Jake?<</dialog>>
<<dialog "brother">>(one hand up hold on)
(into mic) Yeah, okay, I'll push one sec
(to you, pulling headset down)
What?<</dialog>>
<<dialog "player">>Just checking you're alive.<</dialog>>
<<dialog "brother">>(back on headset) I'm fine. Close the door?<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's between games. A rare window where there's no noise from his setup.<</narrative>>
<<dialog "brother">>(stretching loudly) Ugh.
(noticing you at the door)
How long have you been there?<</dialog>>
<<dialog "player">>Two seconds. How long have you been gaming?<</dialog>>
<<dialog "brother">>(checking the time, alarmed) Oh. That's...
(quickly)
I'm going to bed soon.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He gets killed in-game right as you come in. The worst timing. He pulls the headset off.<</narrative>>
<<dialog "brother">>(exhausted) Today has been the worst day.<</dialog>>
<<dialog "player">>School or the game?<</dialog>>
<<dialog "brother">>(considering) Both. At the same time. Continuously.<</dialog>>
<<dialog "player">>Do you want to talk about it?<</dialog>>
<<dialog "brother">>(putting the headset back on) No. Maybe later.
(pausing)
Maybe.<</dialog>>`,
friendship: 2, trust: 1
}
],
nightMode: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's on his phone in bed, lamp off, just the screen glow.<</narrative>>
<<dialog "player">>(quietly) Jake. It's late.<</dialog>>
<<dialog "brother">>(not looking up) I know.<</dialog>>
<<dialog "player">>School tomorrow.<</dialog>>
<<dialog "brother">>(phone going face-down) I know. I'm just... winding down.
(quiet)
This is part of it.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>His light is still on when it shouldn't be. He looks slightly caught when you check.<</narrative>>
<<dialog "brother">>I'm not asleep but I was going to be. Soon. That's the plan.<</dialog>>
<<dialog "player">>It's midnight, Jake.<</dialog>>
<<dialog "brother">>(defensively) I know what time it is.
(a rueful beat)
I got into something online. I'll go to sleep now.
(not turning off the lamp yet)<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He actually looks a little rough. Tired eyes. The particular tiredness of someone who hasn't been sleeping enough for weeks.<</narrative>>
<<dialog "player">>You look tired.<</dialog>>
<<dialog "brother">>(without energy to deflect) Yeah.
(just sitting there for a moment)
I'm okay though. It's just school. It's always just school.<</dialog>>
<<dialog "player">>You can talk to me if you need to.<</dialog>>
<<dialog "brother">>(quiet, softer than expected) I know.
(picking up his phone)
Thanks.<</dialog>>`,
friendship: 2, trust: 2
}
],
procrastinating: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's clearly supposed to be doing something else. The textbook is open but facing the wrong direction.<</narrative>>
<<dialog "player">>Finished your homework?<</dialog>>
<<dialog "brother">>(pause) Define finished.<</dialog>>
<<dialog "player">>Jake.<</dialog>>
<<dialog "brother">>(defensive) I'll do the rest in the morning. I work better in the morning.<</dialog>>
<<dialog "player">>You never work in the morning.<</dialog>>
<<dialog "brother">>(weakly) I might start.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's watching YouTube, volume low, guilty expression when he sees you.<</narrative>>
<<dialog "brother">>I did most of it.<</dialog>>
<<dialog "player">>I didn't say anything.<</dialog>>
<<dialog "brother">>(pointing at you) You were going to.
(turning off his monitor)
Fine. I'll do the last part.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He sighs loudly at whatever he's reading. You didn't ask.<</narrative>>
<<dialog "brother">>I have to read forty pages of this book by Friday.
(showing you a very thick book)
It's about the economy in 1930. Nobody should have to read about the economy in 1930.<</dialog>>
<<dialog "player">>I can help you if you need it.<</dialog>>
<<dialog "brother">>(looking at you, considering) ...How much do you know about 1930?<</dialog>>`,
friendship: 2, trust: 1
}
]
},
fhLivingroom_school_afternoon: {
tvZone: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's on the sofa watching something loud on his laptop, headphones in. Moved to the living room to be near people without talking to them.<</narrative>>
<<dialog "player">>(sitting nearby) What are you watching?<</dialog>>
<<dialog "brother">>(taking one earbud out) What?<</dialog>>
<<dialog "player">>What are you watching?<</dialog>>
<<dialog "brother">>(showing you two seconds of it) It's this guy. He does stupid stuff. It's funny.
(earbud back in)<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He commandeered the TV before anyone else could. Something is on that he's pretending to watch while mostly on his phone.<</narrative>>
<<dialog "player">>Are you watching this?<</dialog>>
<<dialog "brother">>(not looking up from his phone) Yes.<</dialog>>
<<dialog "player">>You haven't looked at the screen in seven minutes.<</dialog>>
<<dialog "brother">>(glancing at the TV) I'm listening to it. That counts.
(back to his phone)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's on the sofa, legs up, and actually glances when you sit down.<</narrative>>
<<dialog "brother">>You can put something on if you want. I'm not watching anything.<</dialog>>
<<dialog "player">>Really?<</dialog>>
<<dialog "brother">>(with effort) Yeah. Whatever.
(not moving his legs off the sofa, though)<</dialog>>`,
friendship: 2
}
],
banter: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He says something slightly stupid, entirely confident.<</narrative>>
<<dialog "brother">>I could beat you at any game. Any. You pick.<</dialog>>
<<dialog "player">>Any game?<</dialog>>
<<dialog "brother">>(immediately) Well almost any.
(backtracking)
What were you thinking?<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He looks at what you're doing with mild disdain.<</narrative>>
<<dialog "brother">>You know there's a faster way to do that, right?<</dialog>>
<<dialog "player">>Is there?<</dialog>>
<<dialog "brother">>(crossing the room to show you) Yeah, look
(explaining something very basic with complete confidence)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's mid-snack, completely unbothered.<</narrative>>
<<dialog "brother">>You know what? I'd be good on a game show.
(eating)
I'm quick under pressure.<</dialog>>
<<dialog "player">>You froze when Dad asked you what you wanted for dinner last week.<</dialog>>
<<dialog "brother">>(a beat) That's different. That's a high-stakes question.
(eating more chips)<</dialog>>`,
friendship: 2
}
],
silence: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>You're both on the sofa, not talking, not watching anything. Just coexisting.<</narrative>>
<<dialog "brother">>(after a while) This is kind of nice actually.<</dialog>>
<<dialog "player">>What is?<</dialog>>
<<dialog "brother">>Just sitting here. Not having to do anything.
(slight pause)
Don't make it weird.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's sprawled on the sofa doing nothing, looking at the ceiling.<</narrative>>
<<dialog "player">>You look like a crime scene.<</dialog>>
<<dialog "brother">>(not moving) Thanks.
(a long pause)
The ceiling in this room is better than the ceiling in my room. More interesting.
(genuinely philosophical)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's in the room doing very little. He looks up briefly.<</narrative>>
<<dialog "brother">>I'm not in a talking mood.<</dialog>>
<<dialog "player">>Okay.<</dialog>>
<<dialog "brother">>(a beat) But you can sit here if you want.
(back to looking at nothing)<</dialog>>`,
friendship: 1
}
]
},
fhLivingroom_school_evening: {
tvNight: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He materialises from his room to watch something. Unusual, but here he is.<</narrative>>
<<dialog "brother">>(sitting) Is this a thing you've started watching?<</dialog>>
<<dialog "player">>Yeah, a few episodes in. Want to catch up?<</dialog>>
<<dialog "brother">>(considering) Just tell me what happened so far. I'll pick it up.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He comes down claiming he just wants a drink, but has been on the sofa for twenty minutes.<</narrative>>
<<dialog "player">>You staying?<</dialog>>
<<dialog "brother">>(not admitting it) I'm just sitting for a bit.
(five minutes later, fully engaged in whatever is on)
Wait what happened to that character?<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's next to you on the sofa, relatively at peace. Something good is on.<</narrative>>
<<dialog "brother">>(quietly) This is actually good.<</dialog>>
<<dialog "player">>Told you.<</dialog>>
<<dialog "brother">>(slightly grumpy) You didn't tell me anything. I just watched it and decided.
(but he's not going anywhere)<</dialog>>`,
friendship: 2
}
],
familyMoment: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's near you but not quite with you. Phone in hand, not really using it.<</narrative>>
<<dialog "brother">>(out of nowhere) Do you think we'll stay here? Like, for a long time?<</dialog>>
<<dialog "player">>I don't know. Probably?<</dialog>>
<<dialog "brother">>(quiet for a moment) Okay.
(back to his phone)<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He observes the new living room with teenager-level existential energy.<</narrative>>
<<dialog "brother">>It still doesn't feel like our house.<</dialog>>
<<dialog "player">>It's getting there.<</dialog>>
<<dialog "brother">>Yeah, I guess.
(glancing at you)
Does it feel like home to you yet?<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He laughs at something on his phone and shows you the screen automatically. Before he thinks to stop himself.<</narrative>>
<<dialog "brother">>Look at this.
(shows you something chaotic online)<</dialog>>
<<dialog "player">>(looking) That's chaotic.<</dialog>>
<<dialog "brother">>(already putting his phone away, slightly embarrassed he shared) Yeah. It's stupid. Never mind.
(but smiling)<</dialog>>`,
friendship: 2
}
],
restless: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He paces slightly, picking things up and putting them down. Too wired from gaming to settle.<</narrative>>
<<dialog "player">>You good?<</dialog>>
<<dialog "brother">>(picking up a TV remote and pretending that's what he came for) Yeah. Fine. Just
(puts the remote down)
I lost a really important match today. In the game.
(like it matters because it does to him)<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's texting rapidly and muttering under his breath.<</narrative>>
<<dialog "player">>Who are you texting?<</dialog>>
<<dialog "brother">>(defensively) People.
(realising that's not an answer)
Friends. From school. Some of them.
(texting continues)<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He comes in just to say goodnight. Which he almost never does.<</narrative>>
<<dialog "brother">>(in the doorway) I'm going to bed.<</dialog>>
<<dialog "player">>Night.<</dialog>>
<<dialog "brother">>(standing there a second longer than necessary)
...Yeah.
(going upstairs)<</dialog>>`,
friendship: 2, love: 1
}
]
}
}>>/* ==========================================
BROTHER TALK TOPICS SCHOOL PHASE, LEVEL 2
phase === "school" && friendshipLevel === 2
SCHOOL L2 TONE:
→ He trusts you more lets things slip occasionally
→ Slightly less defensive, still deflects with humour
→ Actually asks for help (huge deal for him)
→ Rare unguarded moments: school anxiety, friends, what he cares about
→ You're not just his sister, you're one of like three people he tolerates
→ Stat gains: friendship +2-3, trust +1-2, love +1 on rare genuine moments
========================================== */
<<set setup.brotherTopics.school.level2 = {
fhKitchen_school_morning: {
morning: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's at the counter and holds out the coffee pot toward you as you walk in. First time he's ever done that.<</narrative>>
<<dialog "brother">>You want some?<</dialog>>
<<dialog "player">>(surprised) Yeah. Thanks.<</dialog>>
<<dialog "brother">>(pouring, business-like) You looked tired yesterday. Figured you probably needed it.
(sliding the mug over)<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's actually sitting at the table. Rare, in the morning.<</narrative>>
<<dialog "brother">>(eating properly) I've been getting up ten minutes earlier. It's better.
(with the energy of someone who discovered fire)
You can actually eat breakfast when you're not running for the bus.<</dialog>>
<<dialog "player">>You've been doing that for years and this is news to you?<</dialog>>
<<dialog "brother">>(mildly) Everyone matures at their own pace.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He lingers a bit longer than usual before leaving.<</narrative>>
<<dialog "brother">>(at the door) Hey so I have that presentation thing today.
(casual, but clearly not casual)
It's not a big deal or whatever.<</dialog>>
<<dialog "player">>You'll be fine.<</dialog>>
<<dialog "brother">>(absorbing this) Yeah.
(a beat)
Okay. Yeah. See you later.
(going, slightly lighter)<</dialog>>`,
friendship: 3, trust: 2, love: 1
}
],
confiding: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He starts talking while pouring cereal, like he needs to say it before he thinks better of it.<</narrative>>
<<dialog "brother">>There's this guy at school not like bullying or whatever, it's not that he just keeps making comments and everyone thinks it's funny.
(not looking at you)
I don't really care.<</dialog>>
<<dialog "player">>Jake.<</dialog>>
<<dialog "brother">>(quiet)
...It's annoying, is all.<</dialog>>
<<dialog "player">>Have you talked to anyone?<</dialog>>
<<dialog "brother">>(shutting it down) I'm fine. I just wanted to say it out loud.
(eating quickly)
Forget it. I'm fine.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's been quiet for a bit when he says:<</narrative>>
<<dialog "brother">>I think I'm actually making friends at school. Maybe.
(immediately hedging)
One person for sure. Two maybe. It's early days.<</dialog>>
<<dialog "player">>(genuinely) That's good.<</dialog>>
<<dialog "brother">>(nearly smiling) Yeah.
(getting up)
Don't tell Mom and Dad, it'll turn into a whole thing.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He mumbles something while staring at his cereal.<</narrative>>
<<dialog "brother">>I kind of like it here. The house. The area.
(defensive preempt)
Don't make it weird.<</dialog>>
<<dialog "player">>I won't. I like it too.<</dialog>>
<<dialog "brother">>(nodding, eating)
Good.<</dialog>>`,
friendship: 3, love: 1
}
],
sendoff: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He gives you a look on the way out. Almost conspiratorial.<</narrative>>
<<dialog "brother">>(quietly) If Mom asks, I already finished my English thing.
(pause)
Which I'm going to finish. On lunch. I have a plan.<</dialog>>
<<dialog "player">>Jake.<</dialog>>
<<dialog "brother">>(leaving) I've got it!<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>On his way out, he stops. Turns back.<</narrative>>
<<dialog "brother">>Hey did we have a thing with the people next door? I thought I heard Mom say something.<</dialog>>
<<dialog "player">>I don't think so. Why?<</dialog>>
<<dialog "brother">>(looking relieved) No reason. Just checking.
(going)
Cool. See you.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He appears in the doorway ready to go, spots you, and nods. An actual nod. Not the teenager look-through.<</narrative>>
<<dialog "brother">>Later.<</dialog>>
<<dialog "player">>Later. Good luck today.<</dialog>>
<<dialog "brother">>(a small surprised beat he didn't expect that) ...Yeah. Thanks.
(going, slightly awkward about having liked it)<</dialog>>`,
friendship: 3, love: 1
}
]
},
fhKitchen_school_evening: {
realTalk: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's at the table after dinner, not rushing off. That's new.<</narrative>>
<<dialog "brother">>So that test I was dreading?
(pause)
I think I did okay. Like, actually okay.<</dialog>>
<<dialog "player">>Yeah?<</dialog>>
<<dialog "brother">>(working to contain it) I don't want to say it was good until I see the mark. But it probably wasn't terrible.
(under his breath)
I think I actually got it.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He helps clear the table. Unprompted. Actual help. Something has shifted.<</narrative>>
<<dialog "player">>Thanks for clearing up.<</dialog>>
<<dialog "brother">>(stacking plates) Yeah. It's not a big deal.
(a pause)
How was your day?<</dialog>>
<<dialog "player">>(surprised he asked) Actually... good.<</dialog>>
<<dialog "brother">>(nodding, moving to the sink) Cool.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He sits at the kitchen table after everyone else has gone. Looks like he's waiting for you to come back down.<</narrative>>
<<dialog "brother">>Hey. So there's this thing at school. Like a club I might join.
(very casual)
It's probably stupid.<</dialog>>
<<dialog "player">>What kind of club?<</dialog>>
<<dialog "brother">>(slightly defensive) Don't laugh.
(bracing himself)
It's a strategy game club. Board games and stuff. They meet once a week.<</dialog>>
<<dialog "player">>That sounds actually fun.<</dialog>>
<<dialog "brother">>(suspicious) You're not going to say anything embarrassing?<</dialog>>
<<dialog "player">>No. You should go.<</dialog>>
<<dialog "brother">>(quiet for a second) I think I will.
(standing)
Don't tell anyone.<</dialog>>`,
friendship: 3, trust: 2
}
],
helping: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He comes to the kitchen with his laptop open, which almost never happens outside his room.<</narrative>>
<<dialog "brother">>Can I work in here for a bit?<</dialog>>
<<dialog "player">>Yeah, of course.<</dialog>>
<<dialog "brother">>(sitting) It's too quiet in my room.
(opening his stuff)
Don't talk to me though. Working.<</dialog>>
<<narrative>>He works. Actually works. For the next thirty minutes he's still there.<</narrative>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He shows you something on his phone. His draft essay, sent without explanation.<</narrative>>
<<dialog "brother">>Just. If you have a second.
(already looking away)
You don't have to.<</dialog>>
<<dialog "player">>(reading) It's good, Jake. The second paragraph especially.<</dialog>>
<<dialog "brother">>(trying not to look pleased) It's probably fine. I just needed a second pair of eyes.
(taking the phone back)
Thanks.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He makes two cups of tea and brings one to you without being asked.<</narrative>>
<<dialog "brother">>(setting it down) I don't know how you take it. I guessed.<</dialog>>
<<dialog "player">>This is exactly right.<</dialog>>
<<dialog "brother">>(surprised) Oh.
(sitting down)
Cool. I guessed.<</dialog>>`,
friendship: 3, love: 1
}
],
moment: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He looks at you for a second and says something that costs him something to say:<</narrative>>
<<dialog "brother">>I'm glad you're here. Like. In this house. With us.
(immediately)
That sounds weird. Forget I said that.<</dialog>>
<<dialog "player">>It doesn't sound weird.<</dialog>>
<<dialog "brother">>(getting up, embarrassed) It did, slightly. Whatever.
(going to his room, quickly)<</dialog>>`,
friendship: 3, love: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He mentions something small from childhood. Just drops it into conversation out of nowhere.<</narrative>>
<<dialog "brother">>Do you remember that time we had that fish? The one that lasted like three weeks?<</dialog>>
<<dialog "player">>Goldie?<</dialog>>
<<dialog "brother">>(nodding) Yeah. The best fish.
(quiet for a moment)
I was actually sad when it died. I never told anyone that.<</dialog>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He sits at the kitchen table not doing anything. Just being in the same space.<</narrative>>
<<dialog "brother">>(after a long quiet) You know you can knock on my door if you need something. Right? Like, actually knock.
(very casual, not making it a big deal)
I mean I might be mid-game, but still.<</dialog>>
<<dialog "player">>Thanks, Jake.<</dialog>>
<<dialog "brother">>(already looking at his phone) Yeah. Whatever.<</dialog>>`,
friendship: 3, trust: 2, love: 1
}
]
},
fhBrotherRoom_school_afternoon: {
gaming: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He waves you in. Actually waves you in, instead of ignoring you.<</narrative>>
<<dialog "brother">>Come watch this. I'm about to do something stupid.
(fully focused on the screen)
Watch. Watch
(something goes wrong)
Ugh. Okay, watch it again, this time it'll work.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's in a quiet moment between lobbies and actually turns to face you.<</narrative>>
<<dialog "brother">>Do you want to play something? Like, actually play. Not just watch.
(braced for a no)
We don't have to.<</dialog>>
<<dialog "player">>Yeah, okay.<</dialog>>
<<dialog "brother">>(pulling a spare controller from under the desk like he had it ready) Cool.
(matter of fact)
I'll go easy on you. For the first five minutes.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He pulls a clip up on his screen. Something genuinely funny that happened in the game.<</narrative>>
<<dialog "brother">>Okay so look at this. Wait for the end.
(you watch it)
Right?! Like how does that even happen.<</dialog>>
<<dialog "player">>(laughing) That's so dumb.<</dialog>>
<<dialog "brother">>(grinning, pleased) I know. I've watched it like six times.<</dialog>>`,
friendship: 3, love: 1
}
],
opening: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's doing something at his desk and says without turning around:<</narrative>>
<<dialog "brother">>I'm actually kind of good at this subject in school. Like genuinely good. Graphics design stuff.
(a pause)
Nobody knows that. I don't talk about it.<</dialog>>
<<dialog "player">>You should talk about it. That's cool.<</dialog>>
<<dialog "brother">>(quickly) It's not that big a deal.
(carries on working, but clearly glad he said it)<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He shows you something he made. A design. A small project. Something from school or just for himself.<</narrative>>
<<dialog "brother">>(showing you his screen, very casual) Tell me if it looks bad.
(not looking at you while you look)<</dialog>>
<<dialog "player">>(genuinely) Jake, this is really good.<</dialog>>
<<dialog "brother">>(still not looking) It's alright. I might change the colours.
(pause)
But like... thanks.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He asks you something genuine that he's been sitting on.<</narrative>>
<<dialog "brother">>What do you actually do? Like, as a job or whatever?
(pretending it's casual)
I never actually asked.<</dialog>>
<<dialog "player">>(explaining)<</dialog>>
<<dialog "brother">>(listening more carefully than he lets on) Huh.
(turning back to his screen)
That's actually kind of interesting.<</dialog>>`,
friendship: 3, trust: 1
}
],
sibling: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He throws a cushion at you from across the room for no reason. When you throw it back, he catches it and grins.<</narrative>>
<<dialog "brother">>Nice throw.<</dialog>>
<<dialog "player">>Nice catch.<</dialog>>
<<dialog "brother">>(spinning back to his chair) I'm an athlete.
(resumes gaming, content)<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He moves a pile of stuff off his second chair without being asked. Making room for you.<</narrative>>
<<dialog "brother">>(not commenting on it) You can sit there if you want. I'm not going anywhere for a while.<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(already back to gaming) Yeah.<</dialog>>
<<narrative>>You stay for a while. It's easy company.<</narrative>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He says something completely out of nowhere:<</narrative>>
<<dialog "brother">>You know people at school think I'm funny?
(not a brag, more like he's surprised by it)
Like, in a good way.<</dialog>>
<<dialog "player">>I know you're funny.<</dialog>>
<<dialog "brother">>(caught off guard by that) Oh.
(a pause)
Cool.<</dialog>>`,
friendship: 3, love: 1
}
]
},
fhBrotherRoom_school_evening: {
vulnerable: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's already in bed, lights dim, and says something when you check the hall:<</narrative>>
<<dialog "brother">>(from the bed, quiet) Hey. You busy?<</dialog>>
<<dialog "player">>No. What's up?<</dialog>>
<<dialog "brother">>(a long pause) Nothing. Just wanted to know.
(smaller voice)
Good night.<</dialog>>
<<dialog "player">>Night, Jake.<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's off his gaming setup. Unusual for this hour. He's just lying on the bed staring at his phone.<</narrative>>
<<dialog "brother">>(without leading up to it) Is it normal to just feel kind of... heavy sometimes? Not like sad. Just heavy.
(immediately)
Forget it. That's a stupid question.<</dialog>>
<<dialog "player">>No, it's not. It's normal.<</dialog>>
<<dialog "brother">>(quietly, after a moment) Okay.
(back to his phone, but slower)<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He actually asks you to sit down. This never happens.<</narrative>>
<<dialog "brother">>(moving stuff off the second chair) Sit for a sec.
(when you do)
I just wanted someone in the room for a bit. I don't know.
(back to his screen, smaller than usual)
You can go in like five minutes if you want.<</dialog>>`,
friendship: 3, love: 2, trust: 2
}
],
lateNight: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's still up. On his own terms, not avoiding sleep.<</narrative>>
<<dialog "brother">>(as you pass the open door) Hey. What are you up for?<</dialog>>
<<dialog "player">>Water. You?<</dialog>>
<<dialog "brother">>(leaning back in his chair) Couldn't sleep. Might as well do something.
(a pause)
You okay?<</dialog>>
<<dialog "player">>Yeah. You?<</dialog>>
<<dialog "brother">>Yeah.
(genuinely)
Yeah, I think so.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>His light is off but he's awake. You can tell.<</narrative>>
<<dialog "player">>(quietly) Jake. You good?<</dialog>>
<<dialog "brother">>(after a pause) Yeah. Just thinking.
(quiet)
It's fine. Go to sleep.<</dialog>>
<<dialog "player">>You too.<</dialog>>
<<dialog "brother">>(so quietly you almost miss it) Thanks for checking.<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's watching something on his laptop with one earphone in. He pats the edge of the bed when you appear in the doorway.<</narrative>>
<<dialog "brother">>You can watch this if you haven't seen it. It's good.
(making room without making a thing of it)<</dialog>>
<<dialog "player">>(sitting) What is it?<</dialog>>
<<dialog "brother">>Shh. Watch.<</dialog>>`,
friendship: 3, love: 1
}
],
caring: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He looks over at you out of nowhere and says:<</narrative>>
<<dialog "brother">>You looked stressed today. At dinner.<</dialog>>
<<dialog "player">>Did I?<</dialog>>
<<dialog "brother">>(back to his screen) Yeah. You okay?<</dialog>>
<<dialog "player">>I'm okay.<</dialog>>
<<dialog "brother">>(nodding, accepting it) Cool.
(after a moment)
Let me know if you're not.<</dialog>>`,
friendship: 3, love: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He'd heard you having a rough moment earlier. Now he pretends he didn't, but leaves a snack outside your room. You find it later. Tonight he just acts normal.<</narrative>>
<<dialog "brother">>(when you pass) Hey. There's stuff in the kitchen if you want it.
(completely vague on purpose)
I had extra. Whatever.<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He says goodnight properly. Not mumbled from behind a closed door. Standing in the hallway.<</narrative>>
<<dialog "brother">>Night.
(meeting your eyes briefly)
Sleep well.<</dialog>>
<<dialog "player">>You too.<</dialog>>
<<dialog "brother">>(going into his room, voice already retreating) Yeah.<</dialog>>`,
friendship: 3, love: 2
}
]
},
fhLivingroom_school_afternoon: {
together: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's waiting in the living room, which he never does, and when you come down he says:<</narrative>>
<<dialog "brother">>There's this film I want to watch. It's probably not your kind of thing.
(obviously setting it up so you'll say yes)<</dialog>>
<<dialog "player">>I'll watch it.<</dialog>>
<<dialog "brother">>(relief he didn't show) Cool. I'll get the snacks.
(already heading to the kitchen, mission accepted)<</dialog>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He sits close enough to you on the sofa that it's deliberate. A teenager's way of saying he wants company.<</narrative>>
<<dialog "brother">>(watching something) This part's funny. Wait for it.
(glances at you to check you're watching, then looks back)<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's laughing at something on his phone and without thinking shows you the screen.<</narrative>>
<<dialog "brother">>(genuinely amused) Look at this okay maybe you won't find it as funny but look<</dialog>>
<<dialog "player">>(looking) That's so stupid.<</dialog>>
<<dialog "brother">>(grinning) Exactly!
(and then he shows you three more things without being asked)<</dialog>>`,
friendship: 3, love: 1
}
],
advice: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He asks something he's been thinking about. Not as casually as he's pretending.<</narrative>>
<<dialog "brother">>When you were in school like, high school did you know what you wanted to do?<</dialog>>
<<dialog "player">>Not really. Not yet.<</dialog>>
<<dialog "brother">>(absorbing this, relieved) Okay. Good. That's fine then.
(back to the TV)<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He brings up something that clearly bugged him:<</narrative>>
<<dialog "brother">>Is it normal for teachers to just... pick on the same person every class?
(trying to be objective about it)<</dialog>>
<<dialog "player">>What kind of picking on?<</dialog>>
<<dialog "brother">>(just wants the answer) Just like always asking them questions when it's obvious they don't want to be called on. Stuff like that.<</dialog>>
<<dialog "player">>Is it you?<</dialog>>
<<dialog "brother">>(beat) ...Sometimes. Yeah.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He mentions something that he'd normally never say out loud:<</narrative>>
<<dialog "brother">>I'm actually pretty good at making people laugh. In the right situations.
(pause)
I just I forget to use it. When I'm nervous I kind of shut down.<</dialog>>
<<dialog "player">>That's really common.<</dialog>>
<<dialog "brother">>(quietly) Yeah?
(thoughtful)
How do you not do that?<</dialog>>`,
friendship: 3, trust: 2
}
],
normal: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's just in the room with you, easy, no agenda.<</narrative>>
<<dialog "brother">>(not looking up) You know I'd roast you if you were doing it wrong. Right?<</dialog>>
<<dialog "player">>Doing what wrong?<</dialog>>
<<dialog "brother">>(glancing over) Whatever you're doing. Just in general. I'd tell you.<</dialog>>
<<dialog "player">>Is that... your version of saying you've got my back?<</dialog>>
<<dialog "brother">>(back to his phone) Don't analyse it.<</dialog>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He offers you half of whatever he's eating. Just reaches it across without saying anything.<</narrative>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(watching TV) There was loads.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He grins at something and you grin back even without knowing why, and for a second it's just easy. Normal. Good.<</narrative>>
<<dialog "brother">>(shaking his head at the TV) This show is ridiculous.<</dialog>>
<<dialog "player">>Completely.<</dialog>>
<<dialog "brother">>Still good though.<</dialog>>
<<dialog "player">>Yeah. Still good.<</dialog>>`,
friendship: 3, love: 1
}
]
},
fhLivingroom_school_evening: {
comfortable: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's down for the whole evening. Not rushing off. Fully settled in.<</narrative>>
<<dialog "brother">>(comfortable, legs over the sofa arm) This is actually my favourite part of the day.
(not embarrassed to admit it)
Just. No school. No homework. Just here.<</dialog>>
<<dialog "player">>Same.<</dialog>>
<<dialog "brother">>(a small content sound. Just being.)<</dialog>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He starts talking in the dark of the living room, the TV reflected in his eyes:<</narrative>>
<<dialog "brother">>I thought moving here was going to be the worst thing.
(watching the screen)
It's not.
(beat)
For the record.<</dialog>>
<<dialog "player">>For the record noted.<</dialog>>
<<dialog "brother">>(nearly smiling)<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He stays up later than usual just because you're both here. Neither of you mentions it.<</narrative>>
<<dialog "brother">>(eventually, very tired) Okay. I actually need to go to bed now.
(getting up)
This was good though.<</dialog>>
<<dialog "player">>Yeah. Night.<</dialog>>
<<dialog "brother">>(going upstairs)
Night.<</dialog>>`,
friendship: 3, love: 2
}
],
talkingAboutThings: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>The TV is muted. He's turned toward you slightly. That's different.<</narrative>>
<<dialog "brother">>Can I ask you something weird?<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "brother">>Do you think Dad's doing okay? Like, with the new job and everything?<</dialog>>
<<dialog "player">>I think so. He seems better lately.<</dialog>>
<<dialog "brother">>(nodding, sitting back) Okay. Good. I wasn't sure.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's been quiet and then says:<</narrative>>
<<dialog "brother">>I had this weird thing where I realised I actually like living here. Like, the house itself. And I felt guilty about it?
(confused at his own feeling)
Like I was supposed to miss the old place more.<</dialog>>
<<dialog "player">>That's not weird. It just means you've settled in.<</dialog>>
<<dialog "brother">>(accepting this slowly) Yeah. Okay. That makes sense.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He says something that takes guts for a teenager:<</narrative>>
<<dialog "brother">>I'm glad you're around. Like, generally.
(staring at the TV, very casual delivery)
You're a real person. Most people aren't.
(before you can respond)
That's a compliment, by the way.<</dialog>>`,
friendship: 3, love: 2
}
],
brotherly: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He teases you. Properly, affectionately. Knowing you can take it.<</narrative>>
<<dialog "brother">>(very seriously) I just want you to know that your taste in films is objectively bad.
(holds up a hand before you argue)
I still watch them with you. Because I love you. But bad.<</dialog>>
<<dialog "player">>Did you just say you love me?<</dialog>>
<<dialog "brother">>(immediately) I said I watch bad films with you. That's different. Different thing.<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He does something for you without being asked. Grabs a blanket. Switches the lamp on. Adjusts the TV so you can see better. Small seamless domestic care.<</narrative>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(settled back in, casual) Yeah.<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/school/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's the one who refills your drink this time. Comes back with two without being asked.<</narrative>>
<<dialog "player">>Since when do you<</dialog>>
<<dialog "brother">>(sitting back down) Don't make it a thing.<</dialog>>
<<dialog "player">>(smiling) I'm not making it a thing.<</dialog>>
<<dialog "brother">>(very firmly) Good. Glad we agree.<</dialog>>`,
friendship: 3, love: 1
}
]
}
}>>/* ==========================================
BROTHER TALK TOPICS VACATION PHASE, LEVEL 1
phase === "vacation" && friendshipLevel === 1
VACATION TONE (L1):
→ School is OFF. He's immediately in full relax mode.
→ Late nights, late mornings, no schedule, junk food logic
→ Slightly more available but just as guarded
→ Boredom = interesting conversations happen by accident
→ New energy: backyard appears, more morning presence
→ Still "don't invade my space" but the wall is slightly lower
8 CONTEXTS:
fhBrotherRoom_vacation_morning _afternoon _evening
fhKitchen_vacation_morning _evening
fhLivingroom_vacation_morning _afternoon _evening
fhBackyard_vacation_afternoon _evening
========================================== */
<<set setup.brotherTopics.vacation = setup.brotherTopics.vacation || {}>>
<<set setup.brotherTopics.vacation.level1 = {
fhBrotherRoom_vacation_morning: {
rarelyAwake: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's technically awake. The evidence: his eyes are partially open and there's a game loading on his screen.<</narrative>>
<<dialog "player">>(at the door) You're up early?<</dialog>>
<<dialog "brother">>(not fully there) I never went to sleep.
(ambient pause)
What time is it?<</dialog>>
<<dialog "player">>Nine.<</dialog>>
<<dialog "brother">>(processing this for a long moment) Huh.
(that's apparently fine)<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's in the world's most chaotic morning state. Dishevelled hair, hoodie that might be yesterday's, surrounded by empty crisp packets.<</narrative>>
<<dialog "player">>Jake.<</dialog>>
<<dialog "brother">>(looking up from the screen like he forgot there were other people in the house) Oh. Hey.<</dialog>>
<<dialog "player">>When did you last sleep?<</dialog>>
<<dialog "brother">>(sincerely) It's vacation. Sleep is optional.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's been up long enough that he's actually functional. The room has a different energy. Vacation mode settled in.<</narrative>>
<<dialog "brother">>(from his desk, fairly awake) There's nothing to do.
(as a statement of fact)
On vacation. Nothing to do.<</dialog>>
<<dialog "player">>You've been gaming for hours?<</dialog>>
<<dialog "brother">>(looking at his screen) Yeah but it's different when you don't have to stop.<</dialog>>`,
friendship: 1
}
],
justUp: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He materialises, squinting, in his door frame. He's been asleep and just woke up.<</narrative>>
<<dialog "brother">>(croaky) Is there food?<</dialog>>
<<dialog "player">>In the kitchen. Yes.<</dialog>>
<<dialog "brother">>(turning toward the stairs, not fully conscious) Cool.
(calling back)
Thanks.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's just woken up and is sitting on the edge of his bed looking at his phone with the energy of a computer still booting.<</narrative>>
<<dialog "player">>You want anything?<</dialog>>
<<dialog "brother">>(too early for sentences) Nnn.
(long pause)
Water? Please.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He opens his curtains, a historic event, and blinks at the daylight.<</narrative>>
<<dialog "brother">>Is it always this bright?
(genuinely unsure)<</dialog>>
<<dialog "player">>It's literally just a normal day.<</dialog>>
<<dialog "brother">>(pointing at the window) That's a lot of sun though.
(processing)
Huh.<</dialog>>`,
friendship: 2
}
],
gaming: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's in a full vacation gaming lobby, music on, snacks nearby. The long version of this.<</narrative>>
<<dialog "player">>(at the door) How long today?<</dialog>>
<<dialog "brother">>(into his microphone) yeah, hold on (to you) What?<</dialog>>
<<dialog "player">>How long have you been playing?<</dialog>>
<<dialog "brother">>(to mic) Two secs
(to you)
I don't know. It's vacation. What is time.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's mid-game but not seriously. Vacation mode means he can lose and not care.<</narrative>>
<<dialog "brother">>(getting killed in-game) Whatever. I don't care.
(sees you)
Oh hey. What do you need?<</dialog>>
<<dialog "player">>Nothing. Just checking in.<</dialog>>
<<dialog "brother">>(back to game, casual) Cool. I'll be down for lunch. Probably.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He pauses his game. Actually pauses. Then stretches dramatically.<</narrative>>
<<dialog "brother">>Okay I need to not look at a screen for five minutes or my eyes will fall out.
(swivelling his chair toward you)
What's happening in the house today?<</dialog>>
<<dialog "player">>Not much.<</dialog>>
<<dialog "brother">>(nodding) Good. That's correct vacation behaviour.<</dialog>>`,
friendship: 2
}
]
},
fhBrotherRoom_vacation_afternoon: {
bored: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He says it with the weight of the ages:<</narrative>>
<<dialog "brother">>I'm bored.<</dialog>>
<<dialog "player">>Go outside?<</dialog>>
<<dialog "brother">>(as if considering this for the first time) ...In the sun?<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "brother">>(genuine internal debate)
...Maybe.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's horizontal on his bed, staring at the ceiling. The gaming setup is right there. He's not using it.<</narrative>>
<<dialog "player">>(at the door) Okay?<</dialog>>
<<dialog "brother">>(ceiling) I've played too much. My brain is full.
(turning his head)
Is there cake or something?<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's done something different from gaming. A sketchbook is open, which he immediately closes when you see it.<</narrative>>
<<dialog "brother">>(closing it fast) It's nothing. I was bored.
(covering it with a textbook, instinctively)
Don't ask.<</dialog>>`,
friendship: 1, trust: 1
}
],
gaming: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's in a long session, properly in the zone. You knock and he actually lets you in without irritation.<</narrative>>
<<dialog "brother">>(to mic) Give me two minutes (to you) Hey. What's up?<</dialog>>
<<dialog "player">>Nothing. You want food?<</dialog>>
<<dialog "brother">>(already back at the mic but genuinely) Yeah, whatever you've got.
(a bit warmer than normal)
Thanks.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's on a winning streak and it shows. More good energy than usual.<</narrative>>
<<dialog "brother">>(grinning when you come in) I'm having the best session.
(without breaking eye contact with the screen)
Five wins. In a row. Today is good.<</dialog>>
<<dialog "player">>Congrats?<</dialog>>
<<dialog "brother">>Thank you. It's a big deal. Appreciate it.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He gestures at you to sit down with the spare controller in hand.<</narrative>>
<<dialog "brother">>Come on. I'll teach you something new. One game.<</dialog>>
<<dialog "player">>I have other things<</dialog>>
<<dialog "brother">>(very calm) One game. I bought new snacks. Sit down.
(already pressing start)<</dialog>>`,
friendship: 2
}
],
music: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Music is on. Actual speakers, not headphones. He doesn't turn it down when you appear.<</narrative>>
<<dialog "player">>What is this?<</dialog>>
<<dialog "brother">>(barely looking up) Old playlist.
(a pause)
Dad's got similar stuff actually. Don't tell him I know that.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's listening to something on his headphones and not gaming. Just listening. He pulls one side off when you come in.<</narrative>>
<<dialog "brother">>(offering one earbud) Here. Just this track.<</dialog>>
<<dialog "player">>(listening) It's good.<</dialog>>
<<dialog "brother">>(taking it back) Yeah. I know. Found it yesterday.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's drumming quietly on his desk to something. Actually rhythmically good. He stops when he notices you.<</narrative>>
<<dialog "player">>Keep going.<</dialog>>
<<dialog "brother">>(stopping anyway) No. It's cringe.<</dialog>>
<<dialog "player">>It wasn't.<</dialog>>
<<dialog "brother">>(a brief pause, then quietly drumming again, slightly)<</dialog>>`,
friendship: 2
}
]
},
fhBrotherRoom_vacation_evening: {
lateSession: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's in full vacation evening mode. The room is dark except the monitors, music low, snacks within reach.<</narrative>>
<<dialog "player">>(from the door) You're not sleeping?<</dialog>>
<<dialog "brother">>It's vacation. Sleeping's for school days.
(glancing over)
You heading to bed?<</dialog>>
<<dialog "player">>Not yet.<</dialog>>
<<dialog "brother">>(back to the screen) Cool. Door's open if you want company later.
(very casual, very deliberate)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>The monitors are off. He's on his phone in the dark instead.<</narrative>>
<<dialog "brother">>(scrolling) Have you seen that thing about
(stops himself)
Actually, never mind, you probably haven't.<</dialog>>
<<dialog "player">>What thing?<</dialog>>
<<dialog "brother">>(showing you his phone) This. But you need like ten hours of context for it to be funny.<</dialog>>
<<dialog "player">>Try me.<</dialog>>
<<dialog "brother">>(shifting to give you the full story, clearly has wanted to tell someone)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's winding down but not ready for sleep. He's just... sitting. And when you come in he doesn't make you leave.<</narrative>>
<<dialog "brother">>(looking at the ceiling) Good day today. No reason. Just was.
(ambient)<</dialog>>
<<dialog "player">>Yeah. It was.<</dialog>>
<<dialog "brother">>(small exhale, content)
Yeah.<</dialog>>`,
friendship: 2
}
],
youtube: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's deep in a YouTube rabbit hole. You can tell by the unfocused expression.<</narrative>>
<<dialog "brother">>(hearing you) Don't judge me. I started watching a video about ancient Rome and somehow ended up watching fish tank tutorials.<</dialog>>
<<dialog "player">>That's a very specific journey.<</dialog>>
<<dialog "brother">>(scrolling) It felt logical at every step.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's watching something with the volume just audible. A video essay, something long-form.<</narrative>>
<<dialog "player">>What's that one?<</dialog>>
<<dialog "brother">>Some guy explaining why a film from the 90s is secretly about capitalism.
(pause)
I don't know if he's right but he's very convincing.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He shows you his phone, a genuinely bizarre video, and monitors your reaction.<</narrative>>
<<dialog "brother">>Tell me what you think is happening in this video before I explain it.<</dialog>>
<<dialog "player">>(watching, confused) ...I have no idea.<</dialog>>
<<dialog "brother">>(delighted) Nobody does. The creator doesn't know either. That's the whole thing.<</dialog>>`,
friendship: 2
}
],
night: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's very clearly not going to sleep for another three hours. He's at peace with this.<</narrative>>
<<dialog "brother">>(cheerful) I'm going to regret this tomorrow.
(doing it anyway)<</dialog>>
<<dialog "player">>It's vacation.<</dialog>>
<<dialog "brother">>(pointing at you in agreement) Exactly. Thank you.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He calls out from his room as you walk past:<</narrative>>
<<dialog "brother">>Hey. I heard you going past.
(as a way of saying 'come in if you want to')<</dialog>>
<<dialog "player">>(from the hall) Just getting water.<</dialog>>
<<dialog "brother">>Okay.
(a pause)
You can come in though. If you want. I'm not doing anything interesting but.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He says goodnight first. A vacation thing. He's more relaxed about it here.<</narrative>>
<<dialog "brother">>I'm going to actually sleep tonight. Experiment.
(in the doorway)
Night.<</dialog>>
<<dialog "player">>Good luck with that.<</dialog>>
<<dialog "brother">>(pointing down the hall toward his room) Night!<</dialog>>`,
friendship: 2
}
]
},
fhKitchen_vacation_morning: {
slowBreakfast: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's at the kitchen table with a bowl of cereal and his phone. No rush, no school, no bag. The full vacation morning look.<</narrative>>
<<dialog "brother">>(not looking up) Did you know a bear can run thirty miles per hour?<</dialog>>
<<dialog "player">>At nine in the morning?<</dialog>>
<<dialog "brother">>(holding up his phone) I'm doing this thing where I learn a fact a day.
(back to his cereal)
I learned it yesterday. Still thinking about it.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's made proper breakfast. Eggs, toast. The energy of someone who has nothing to rush for.<</narrative>>
<<dialog "player">>You cooked?<</dialog>>
<<dialog "brother">>(pleased with himself) It's vacation. I have time now.
(pushing the extra plate toward you)
There's more if you want.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He comes in, does not look like someone who slept great, makes toast in silence, sits across from you without comment.<</narrative>>
<<dialog "player">>(eventually) Good morning.<</dialog>>
<<dialog "brother">>(not looking up) Is it defined as good when you slept four hours?
(eating anyway)
The toast is good though.<</dialog>>`,
friendship: 1
}
],
planless: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's standing in the kitchen with no particular direction.<</narrative>>
<<dialog "brother">>What are you doing today?<</dialog>>
<<dialog "player">>Not sure. You?<</dialog>>
<<dialog "brother">>(pouring coffee) Same.
(a pause)
We could do something. Or not.
(back to the coffee)
Whatever.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He has his phone out looking at things to do.<</narrative>>
<<dialog "brother">>There's a gaming café thing in town. Or I could just stay here.
(to himself)
Probably just stay here.
(to you)
Do you like gaming cafés?<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's made no plans. He's happy about this.<</narrative>>
<<dialog "brother">>(deeply content) Nothing to do today. Whole day. Nothing.
(sitting down to his breakfast)
This is genuinely the best feeling.<</dialog>>
<<dialog "player">>You'll be bored by noon.<</dialog>>
<<dialog "brother">>(confidently) I will. But right now, in this moment, it's perfect.<</dialog>>`,
friendship: 2
}
],
actualChat: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's at the table and looks up when you come in. Actually meets your eyes first thing.<</narrative>>
<<dialog "brother">>Did you sleep okay?<</dialog>>
<<dialog "player">>(surprised) Yeah. You?<</dialog>>
<<dialog "brother">>(so-so) Six hours. It'll do.
(back to his phone, but lighter than usual)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He pours you a glass of juice just because he was pouring one for himself.<</narrative>>
<<dialog "brother">>(sliding it across) You were going to want some.<</dialog>>
<<dialog "player">>Were you certain?<</dialog>>
<<dialog "brother">>(sitting down) Educated guess.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He said something in his sleep apparently. You overhead him. He looks mortified when you imply it.<</narrative>>
<<dialog "player">>(innocently) You were talking in your sleep last night.<</dialog>>
<<dialog "brother">>(immediately) What? No I wasn't. What did I say?<</dialog>>
<<dialog "player">>Nothing too bad.<</dialog>>
<<dialog "brother">>(deeply suspicious) What did I say.<</dialog>>`,
friendship: 2
}
]
},
fhKitchen_vacation_evening: {
dinner: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's at dinner and actually present. No tomorrow to worry about. He eats slower.<</narrative>>
<<dialog "brother">>Today was alright.
(genuinely)<</dialog>>
<<dialog "player">>Yeah?<</dialog>>
<<dialog "brother">>(shrugging) Didn't do much. But it was fine. That was the whole day and it was fine.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's been in a good mood all day. Vacation agrees with him. You can see it at dinner.<</narrative>>
<<dialog "brother">>(loading his plate) I had the most insane gaming session today.
(sitting down)
Top one percent. They give you a thing. I got the thing.<</dialog>>
<<dialog "player">>Congrats on the thing.<</dialog>>
<<dialog "brother">>(dead serious) Thank you. It matters.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He actually helps set the table. Vacation energy means he's less defensive about being helpful.<</narrative>>
<<dialog "brother">>(putting out plates) Where do these go over here?<</dialog>>
<<dialog "player">>There's fine.<</dialog>>
<<dialog "brother">>(setting them, casual) I don't mind this. Setting the table. It's a contained task.
(sitting down)
I like contained tasks.<</dialog>>`,
friendship: 2, trust: 1
}
],
lateKitchen: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He materialises at 10pm looking for snacks. This is his natural cycle.<</narrative>>
<<dialog "brother">>(opening cupboards) We're out of crisps again.
(finding crackers)
...These will do.
(sitting at the table, apparently staying a while)<</narrative>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's made himself something substantial at an unreasonable hour. No shame.<</narrative>>
<<dialog "player">>It's eleven PM, Jake.<</dialog>>
<<dialog "brother">>(eating a full bowl of pasta) Vacation doesn't have rules about food times.
(genuinely believing this)
This is documented.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He makes extra and brings you some without being asked. It's vague and casual and actually kind.<</narrative>>
<<dialog "brother">>(setting something down near you) I made too much. Just leave it if you don't want it.<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(already heading back to his room) Yeah.<</dialog>>`,
friendship: 2
}
],
chatting: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He sits at the kitchen table after dinner and doesn't immediately leave.<</narrative>>
<<dialog "brother">>Can I ask you something?<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "brother">>(genuinely curious, not leading anywhere weird) What's your earliest memory?
(eating something)
Mine's a supermarket. Which is a weird first memory.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's found a subject he actually wants to talk about. You can tell because he's using full sentences.<</narrative>>
<<dialog "brother">>There's this game mechanic I've been thinking about designing. Like, professionally. Not now. Eventually.
(getting into it)
Tell me if this is stupid: what if the whole point of the game was<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's at the counter and asks:<</narrative>>
<<dialog "brother">>On a scale of one to ten how weird is it that I genuinely enjoy doing the dishes?
(rinsing a mug)
I won't tell anyone. I just think it's calm. Containable.
(drying his hands)
It's a ten isn't it.<</dialog>>
<<dialog "player">>Maybe a six.<</dialog>>
<<dialog "brother">>(accepting) I can live with six.<</dialog>>`,
friendship: 2
}
]
},
fhLivingroom_vacation_morning: {
morning: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's on the sofa with a blanket and his phone. Migrated from his room to here for reasons he hasn't articulated.<</narrative>>
<<dialog "player">>(surprised) You're down here?<</dialog>>
<<dialog "brother">>(already under the blanket) My room gets warm in the morning.
(very settled)
Don't make it weird.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's watching something with the volume low. Morning TV. Some kind of nature documentary.<</narrative>>
<<dialog "player">>When did you become a nature documentary person?<</dialog>>
<<dialog "brother">>(watching) Shh. The crab is about to do something.<</dialog>>
<<dialog "player">>...The crab?<</dialog>>
<<dialog "brother">>(pointing) There. Watch.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's doing absolutely nothing. On the sofa, no phone, no TV. Just being there.<</narrative>>
<<dialog "player">>You okay?<</dialog>>
<<dialog "brother">>(genuinely content) Yeah. I'm just sitting.
(as if this is perfectly normal)
Haven't done this in a while.<</dialog>>`,
friendship: 1
}
],
breakfast: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's got breakfast on the coffee table. Technically Mom would not approve, but she's not here.<</narrative>>
<<dialog "brother">>(pre-empting) I'm going to clean this up. Before you say anything.
(eating)
I always clean it up.<</dialog>>
<<dialog "player">>You left a bowl here last Tuesday.<</dialog>>
<<dialog "brother">>(beat) That was one time.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's got cereal and the TV on. Moves his feet to make room for you without being asked.<</narrative>>
<<dialog "brother">>(not looking from the TV) There's still milk if you want.<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(still watching) Yeah.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's comfortable in a very particular morning-only way. Relaxed before the day has any expectations.<</narrative>>
<<dialog "brother">>(slowly) Vacation morning is a different kind of morning.
(philosophical)
You should be allowed to sit in it.<</dialog>>
<<dialog "player">>I agree.<</dialog>>
<<dialog "brother">>(good)<</dialog>>`,
friendship: 2
}
],
laziness: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's been on the sofa for two hours and shows absolutely no sign of moving.<</narrative>>
<<dialog "player">>Have you moved at all?<</dialog>>
<<dialog "brother">>(checking) I got water. About forty minutes ago.
(settling back)
That counts.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's got the remote. There's nothing on. He just keeps clicking past things.<</narrative>>
<<dialog "brother">>(clicking) There is nothing good on TV in the morning. This is a fact. And I know because I have to find out every vacation.
(still clicking)<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He looks at you with genuine morning bafflement:<</narrative>>
<<dialog "brother">>What do people do in the morning? When they're not going to school and they're not a full adult yet?
(serious question)<</dialog>>
<<dialog "player">>Go outside? Hobby?<</dialog>>
<<dialog "brother">>(considering these options as if for the first time) Hm.
(picks up his phone instead)<</dialog>>`,
friendship: 2
}
]
},
fhLivingroom_vacation_afternoon: {
lounging: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's on the sofa in the fully horizontal position, arm over his face. Not asleep.<</narrative>>
<<dialog "player">>Dead?<</dialog>>
<<dialog "brother">>(arm still over his face) No. Thinking.<</dialog>>
<<dialog "player">>About what?<</dialog>>
<<dialog "brother">>(pause) Nothing in particular.
(moving the arm)
Everything's kind of at once.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's invented a game where he tries to balance the TV remote on his knee.<</narrative>>
<<dialog "player">>(watching) ...What are you doing?<</dialog>>
<<dialog "brother">>(focused) Balancing it. I've been at it for like fifteen minutes.
(it falls off)
Okay but I almost had it that time.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's found the TV again, but this time something is actually on.<</narrative>>
<<dialog "brother">>(without looking away) Come watch this. You'll hate it.
(moving to give you sofa space)
But you'll watch the whole thing anyway.<</dialog>>`,
friendship: 2
}
],
goofing: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's doing impressions of people he knows. Teachers, Dad, himself. Without an audience until you come in.<</narrative>>
<<dialog "player">>(from the doorway) Who was that?<</dialog>>
<<dialog "brother">>(immediately stops) Nobody. Nothing. I was on the phone.<</dialog>>
<<dialog "player">>You weren't on the phone.<</dialog>>
<<dialog "brother">>(beat) ...Do the Mr. Daniels one again or no?<</dialog>>
<<dialog "player">>Yes. Definitely yes.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's started a running commentary on the bad daytime TV.<</narrative>>
<<dialog "brother">>(to the TV) That's not how kitchens work. Real kitchens don't have that much counter space.
(aside to you)
We should watch bad TV together more. I have a lot of opinions.
(back to the TV)
Also that cabinet would fall, the mounting is wrong.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He challenges you to something. A competition, a bet, something low-stakes and dumb.<</narrative>>
<<dialog "brother">>Okay. I bet you fifty pence I can name more countries in sixty seconds than you can.
(completely confident)
You don't have to bet. But you should.<</dialog>>`,
friendship: 2
}
],
sibling: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's watching something and laughs. Actually laughs, properly. And it's easy to be in the same room.<</narrative>>
<<dialog "player">>Good?<</dialog>>
<<dialog "brother">>(still laughing a bit) Yeah. Sorry. It's just this person
(shows you the screen)
Look at this.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>You're both on the sofa doing different things. Parallel existence. Occasionally he'll show you something from his phone, you'll look, you'll both go back.<</narrative>>
<<dialog "brother">>(at some point) This is alright.
(meaning: this, right here, is enough)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He falls asleep on the sofa briefly. Properly asleep for about twenty minutes. Wakes up disoriented and slightly embarrassed.<</narrative>>
<<dialog "brother">>(waking up, groggy) I wasn't I wasn't asleep.<</dialog>>
<<dialog "player">>You were snoring.<</dialog>>
<<dialog "brother">>(sitting up) I breathe loud.
(very firmly)<</dialog>>`,
friendship: 2
}
]
},
fhLivingroom_vacation_evening: {
filmNight: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's set up the living room for a proper film. Blankets, snacks, lights off. Didn't ask anyone, just did it.<</narrative>>
<<dialog "brother">>(when you come in) I'm putting something on. If you want to watch you can, if not I'm watching anyway.
(having sorted everything out)
I got the good crisps.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He passes you something from the snack bowl without you asking.<</narrative>>
<<dialog "brother">>(eyes on the screen) You were going to want this part. Pay attention.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He picks up the remote and completely unconsciously moves over to give you more sofa space when you sit down.<</narrative>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(already looking at the screen) Yeah.
(it's easy)<</dialog>>`,
friendship: 2
}
],
chatty: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He turns the TV off and is just... talking. On occasion, on vacation evenings, this version of him appears.<</narrative>>
<<dialog "brother">>Do you think I'd be good at something creative? Like, professionally?
(not looking at you, at the wall)<</dialog>>
<<dialog "player">>Yeah, I do.<</dialog>>
<<dialog "brother">>(sitting with that for a moment) Okay.
(back to normal)
Cool.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He says something uncharacteristically reflective:<</narrative>>
<<dialog "brother">>I think I've been nicer lately. On vacation.
(sort of noting it)
I think school makes me worse as a person.<</dialog>>
<<dialog "player">>School makes most people worse. That's kind of why vacations exist.<</dialog>>
<<dialog "brother">>(nodding at this wisdom) Yeah. Fair.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He mentions something he wouldn't normally:<</narrative>>
<<dialog "brother">>I tried to draw something today. Like, actually draw. Not just doodle.
(looking at his hands)
It was alright. I don't know.
(pause)
Do you ever do stuff just to see if you're good at it?<</dialog>>`,
friendship: 2, trust: 1
}
],
unwinding: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's fully horizontal, no devices, just existing.<</narrative>>
<<dialog "brother">>(to the ceiling) Good vacation day today. I accomplished nothing and everything was fine.<</dialog>>
<<dialog "player">>Same.<</dialog>>
<<dialog "brother">>(content exhale) Yeah.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's about to head to bed but lingers a minute longer than he needs to.<</narrative>>
<<dialog "brother">>I'm going to sleep at a normal time tonight.
(meaning: I'm going to try)
Night.<</dialog>>
<<dialog "player">>Good luck.<</dialog>>
<<dialog "brother">>(going) Night.
(will probably not sleep at a normal time)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's on the sofa and reaches past you to get something, brushes your shoulder, and doesn't make it weird. It's just easy.<</narrative>>
<<dialog "brother">>(getting what he needed) Sorry.
(back to his phone, completely relaxed)<</dialog>>
<<dialog "player">>It's fine.<</dialog>>
<<dialog "brother">>(not thinking about it) Yeah.<</dialog>>`,
friendship: 2
}
]
},
fhBackyard_vacation_afternoon: {
outside: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's sitting on the back step with his phone, sunglasses on. He's managed to take the gaming lifestyle outside.<</narrative>>
<<dialog "player">>(surprised) You're outside?<</dialog>>
<<dialog "brother">>(phone in hand) My room is hot and somewhere along the way I ended up here.
(not moving)
It's not bad actually.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's found something to kick around. A football, a stone. The particular aimlessness of a teenager with nowhere to be.<</narrative>>
<<dialog "player">>(stepping out) Hey.<</dialog>>
<<dialog "brother">>(kicking the thing) Hey.
(a pause)
You can sit on the step if you want. I'm not doing anything important.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's lying on the grass looking at the sky. Sincerely.<</narrative>>
<<dialog "player">>Jake.<</dialog>>
<<dialog "brother">>(not moving) What.<</dialog>>
<<dialog "player">>What are you doing?<</dialog>>
<<dialog "brother">>(a pause)
Looking at the sky.
(like this is a perfectly normal activity)
Sit down, it's actually good.<</dialog>>`,
friendship: 2
}
],
activities: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's bouncing a ball off the back fence. Rhythmic, meditative. Something he still does from years ago.<</narrative>>
<<dialog "player">>Want company?<</dialog>>
<<dialog "brother">>(catching the ball, considering) You can stand here if you want.
(throws it again)
I'm not talking. I do this to not think.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's found the old badminton set from the move. He's holding the racket like he's not sure if he wants to use it.<</narrative>>
<<dialog "brother">>(to you) Do you play?<</dialog>>
<<dialog "player">>A bit. Why?<</dialog>>
<<dialog "brother">>(handing you a racket) I'm bored enough.
(with the energy of someone who's been bored for two days)
Let's do it.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's helping with something in the garden. Dad asked him and he's doing it with minimal complaint.<</narrative>>
<<dialog "brother">>(not looking up) I'm being helpful. Note it.
(doing the task)
This is me helping. It's a thing I do now.<</dialog>>`,
friendship: 2
}
],
nature: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's noticed something in the garden and is crouching to look at it.<</narrative>>
<<dialog "player">>What is it?<</dialog>>
<<dialog "brother">>(looking closely) Some kind of beetle? It's huge.
(genuinely fascinated, very brief)
Can you look this up.
(pointing at it)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's looking at the neighbour's cat that's got into the garden. Trying to be cool about how much he wants to pet it.<</narrative>>
<<dialog "brother">>(very calm) Don't move. Don't make it weird.
(inching toward the cat)
I just want to see if it'll let me
(cat runs)
Okay. That's fine. I didn't want to anyway.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's in the garden in the sun. Genuinely comfortable. Rare for someone who lives by screen glow.<</narrative>>
<<dialog "brother">>(to the air) The sun's actually nice today.
(looking at his arm)
I think I'm slightly less pale than last week.<</dialog>>
<<dialog "player">>The bar is low.<</dialog>>
<<dialog "brother">>(entirely unbothered) The bar is wherever I set it.<</dialog>>`,
friendship: 2
}
]
},
fhBackyard_vacation_evening: {
sunset: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's on the back step as the light goes golden. He's been out here for a while.<</narrative>>
<<dialog "player">>(coming out) You've been out here a while.<</dialog>>
<<dialog "brother">>(not apologetically) Yeah.
(quiet for a moment)
It gets good around now. The light.<</dialog>>
<<dialog "player">>Since when do you notice the light?<</dialog>>
<<dialog "brother">>(small shrug) I notice things. I just don't usually say it.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's sitting in the garden with nothing in his hands. No phone, no device. Just there.<</narrative>>
<<dialog "player">>(sitting near him) What are you thinking about?<</dialog>>
<<dialog "brother">>(honest) Nothing specific. Just everything kind of at once.
(looks at you briefly)
Do you do that?<</dialog>>
<<dialog "player">>All the time.<</dialog>>
<<dialog "brother">>(back to looking at nothing) Good. I thought it was just me.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's found a spot he likes in the garden. A particular corner. He goes there without explaining.<</narrative>>
<<dialog "player">>(following him) You have a spot?<</dialog>>
<<dialog "brother">>(settling in) Don't tell anyone.
(looking at the fence next door, the sky above)
It's just quieter here.<</dialog>>`,
friendship: 2
}
],
evening: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's on the grass looking up as the first stars come out. Very much not something he'd do in public.<</narrative>>
<<dialog "brother">>(not explaining himself) You can see three already.
(pointing up)
That one first. Then two more.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's kicking about in the garden before coming in. A last bit of air before the evening routine.<</narrative>>
<<dialog "brother">>(to you) Staying out a bit?<</dialog>>
<<dialog "player">>For a while.<</dialog>>
<<dialog "brother">>(sitting on the step nearby, doesn't go in yet) Cool.
(just being out here a bit longer)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He comes outside just as it's getting dark. He's been inside all day and needed air.<</narrative>>
<<dialog "brother">>(breathing it in) Okay. Yeah. That's better.
(sitting on the step)
I forget to go outside sometimes.
(not judgementally, just factually)<</dialog>>
<<dialog "player">>You should do it more.<</dialog>>
<<dialog "brother">>(agreeing slowly) Yeah. Probably.
(stays out for a while)<</dialog>>`,
friendship: 2
}
],
talking: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-3.webp">>
<<image _img "100%">>
<<narrative>>The garden is quiet. He starts talking without making a thing of it:<</narrative>>
<<dialog "brother">>I think I know what I want to do. With like, school and after.
(not looking at you)
I don't know if it's realistic.<</dialog>>
<<dialog "player">>What is it?<</dialog>>
<<dialog "brother">>(a pause)
Something with games. Design or development. I don't know which yet.
(checking your face)
Is that stupid?<</dialog>>
<<dialog "player">>No. It's actually good.<</dialog>>
<<dialog "brother">>(sitting with it) Okay. Good. I think so too.<</dialog>>`,
friendship: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-3.webp">>
<<image _img "100%">>
<<narrative>>Evening makes him say things he wouldn't in daylight:<</narrative>>
<<dialog "brother">>Do you ever feel like you're still figuring out who you are?
(out of nowhere)
Like, even now. Like you haven't finished yet.<</dialog>>
<<dialog "player">>Yeah. Always.<</dialog>>
<<dialog "brother">>(relieved) Good. I thought that was just a me thing.
(back to looking at the stars)<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level1/backyardEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He says it plainly, like it's obvious:<</narrative>>
<<dialog "brother">>You're easy to be around. Like, you don't make things more complicated than they are.
(pause)
Most people do that.<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(standing, heading inside)
That was a compliment, by the way.
(going in)<</dialog>>`,
friendship: 2, trust: 2
}
]
}
}>>/* ==========================================
BROTHER TALK TOPICS VACATION PHASE, LEVEL 2
phase === "vacation" && friendshipLevel === 2
VACATION L2 TONE:
→ He genuinely seeks you out. Won't admit it but does
→ Opens up about future, identity, what he actually cares about
→ Dry humour as warmth: teases you = likes you
→ Backyard at night = his most honest version
→ Real sibling bond. Rare but unmistakeable
→ Stat gains: friendship +3, trust +2, love +1-2
8 CONTEXTS (same as L1):
fhBrotherRoom_vacation_morning _afternoon _evening
fhKitchen_vacation_morning _evening
fhLivingroom_vacation_morning _afternoon _evening
fhBackyard_vacation_afternoon _evening
========================================== */
<<set setup.brotherTopics.vacation.level2 = {
fhBrotherRoom_vacation_morning: {
opening: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He calls from his room as you pass. Not unusual now.<</narrative>>
<<dialog "brother">>Hey. Come look at this.<</dialog>>
<<dialog "player">>(at the door) What is it?<</dialog>>
<<dialog "brother">>(turning his monitor toward you) I made this last night. When I couldn't sleep.
(watching your face)
Tell me honestly if it's bad.<</dialog>>
<<dialog "player">>(looking) Jake. This is really good.<</dialog>>
<<dialog "brother">>(trying not to show how much that matters) I might do more. I don't know.
(turning the screen back)
I just wanted a second opinion.<</dialog>>`,
friendship: 4, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's in a good mood when you pass. You can hear it before you see it.<</narrative>>
<<dialog "brother">>(at his desk, something going right) Hey, good morning.
(pointing at a chair)
Sit for a second. I want to tell you something.<</dialog>>
<<dialog "player">>(sitting) What?<</dialog>>
<<dialog "brother">>That game design thing I was thinking about? I actually started writing it down. Like, properly. Plans and systems and stuff.
(barely containing it)
It might be something real.<</dialog>>`,
friendship: 4, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's already awake when you knock. Unusual. Morning light fills his room.<</narrative>>
<<dialog "brother">>(at his desk, sketchbook open) I've been up since seven. I don't know what happened.
(looking at what he made)
Good morning though.
(a pause, easy)
You want to see what I've been doing?<</dialog>>`,
friendship: 4
}
],
design: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He holds out a printed page. Something he made. Something he designed. Something from the sketchbook.<</narrative>>
<<dialog "brother">>(handing it to you) I printed it. Just to see what it looked like on paper.
(watching you hold it)
You can keep it if you want.<</dialog>>
<<dialog "player">>Jake, this is actually really good.<</dialog>>
<<dialog "brother">>(very quietly) Yeah.
(back to his desk, but settled and warm)
Thanks.<</dialog>>`,
friendship: 4, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He explains what he's working on in more detail than he usually allows himself. The design, the system, the idea behind it.<</narrative>>
<<dialog "brother">>So the mechanic is okay, don't say anything until I finish explaining
(laying it all out, the fastest and most articulate you've heard him)
Does that make sense? Like, does the logic track?<</dialog>>
<<dialog "player">>Yeah. It does. It actually does.<</dialog>>
<<dialog "brother">>(exhaling) Okay. Good. I thought I was going mad.<</dialog>>`,
friendship: 4, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's spent the morning refining something and wants someone to see it.<</narrative>>
<<dialog "brother">>(showing you the screen) I changed the layout. Cleaner.
(you look)
Better, right?<</dialog>>
<<dialog "player">>Definitely better.<</dialog>>
<<dialog "brother">>(nodding, satisfied) Yeah. I thought so.
(already refining it again)<</dialog>>`,
friendship: 4
}
],
morning: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's already made two cups of tea. One is for you. He doesn't explain how he knew you'd be up.<</narrative>>
<<dialog "brother">>(sliding one toward you when you appear in the door) I figured.
(back to his sketchbook)<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(not looking up) You were going to come and bother me eventually. This way we both get tea first.<</dialog>>`,
friendship: 4, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's up early drawing. The sketchbook is out, pencil in hand, window light on the page.<</narrative>>
<<dialog "player">>(at the door) You're drawing?<</dialog>>
<<dialog "brother">>(not closing it) Yeah. I do it in the mornings now. When I can't sleep in.
(shrugging)
It's actually pretty good. The morning thing.<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He says good morning first. Properly. Not the grunt. The actual words.<</narrative>>
<<dialog "brother">>Morning.
(looking up from whatever he's doing)
Sleep okay?<</dialog>>
<<dialog "player">>Yeah. You?<</dialog>>
<<dialog "brother">>(back to his work) Good. Really good actually.
(easy, content)<</dialog>>`,
friendship: 4, love: 1
}
]
},
fhBrotherRoom_vacation_afternoon: {
gaming: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's just won something and he turns around to tell you first. Before anyone, before online friends, before checking the leaderboard.<</narrative>>
<<dialog "brother">>(spinning his chair) I did it. I actually did it.
(genuinely excited in a way he doesn't usually allow)
I ranked up. Top five hundred.<</dialog>>
<<dialog "player">>(matching the energy) That's actually massive.<</dialog>>
<<dialog "brother">>(exhaling, finally letting it out) It took three weeks. Three weeks of ranked matches.
(standing up to stretch)
I wanted to tell you first for some reason. I don't know why.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He hands you a controller without asking.<</narrative>>
<<dialog "brother">>Come on. I want to show you something.
(already loading the mode)
Trust me on this one.<</dialog>>
<<dialog "player">>What is it?<</dialog>>
<<dialog "brother">>(settling in) Just play. You'll see.
(and it turns out to be something you're actually good at, and he's delighted by this)<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He pauses the game when you come in. Full stop, not just a hold-on.<</narrative>>
<<dialog "brother">>Hey. Sit down a sec. I want to ask you something.
(turning his chair)<</dialog>>
<<dialog "player">>What's up?<</dialog>>
<<dialog "brother">>Do you think you can be good at something without having any formal training? Like actually good?
(it's about the game design. It's about him.)<</dialog>>`,
friendship: 4, trust: 2
}
],
talking: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>His gaming setup is off. That's the first signal. He's just sitting at his desk.<</narrative>>
<<dialog "brother">>(when you appear) Hey. You have a minute?<</dialog>>
<<dialog "player">>Yeah. What's going on?<</dialog>>
<<dialog "brother">>(a breath) I've been thinking about after school. Like seriously thinking. Not just avoiding it.
(looking at his desk)
I think I want to actually try to make something.<</dialog>>`,
friendship: 4, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He asks something he's been thinking about for a while:<</narrative>>
<<dialog "brother">>How did you figure out what you were actually good at? Like, when?<</dialog>>
<<dialog "player">>(thinking about it, answering honestly)<</dialog>>
<<dialog "brother">>(listening properly, not performing listening)
(after)
Okay. Yeah. That's actually really helpful.
(nodding)
Thanks.<</dialog>>`,
friendship: 4, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He admits something without framing it as a joke first:<</narrative>>
<<dialog "brother">>I get nervous around people I don't know. Like properly nervous. I freeze up.
(looking at his hands)
But I don't do that with you anymore.
(not a big deal, just true)<</dialog>>
<<dialog "player">>That's good.<</dialog>>
<<dialog "brother">>(quietly) Yeah. It is.<</dialog>>`,
friendship: 4, trust: 3, love: 1
}
],
sibling: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He shows you the notebook. The game design one. Not just the one thing. The whole notebook.<</narrative>>
<<dialog "brother">>(handing it across) Don't read the early stuff. It's embarrassing.
(you look anyway)
Okay fine, you can read the early stuff. Just don't comment on it.<</dialog>>
<<dialog "player">>(reading, genuinely interested) Jake, this is really thought through.<</dialog>>
<<dialog "brother">>(watching your face page by page)
Yeah. I've been working on it for a while.<</dialog>>`,
friendship: 4, trust: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He does an impression of you. Affectionate and slightly uncanny. You didn't know he watched you that closely.<</narrative>>
<<dialog "brother">>(doing you) "Jake, it's late" Jake, have you eaten" "Jake"
(grinning)
That's you. That's what you sound like.<</dialog>>
<<dialog "player">>That's not what I sound like.<</dialog>>
<<dialog "brother">>(completely confident) That is exactly what you sound like. I've been studying.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's calm and comfortable with you in his room now. Doesn't clean up, doesn't get defensive, just lets you be there.<</narrative>>
<<dialog "brother">>(at his desk, drawing) You can sit wherever. I'm just doing this.
(carrying on)
Tell me about your day if you want. I'm listening.
(and he is, in his way, listening)<</dialog>>`,
friendship: 4, love: 1
}
]
},
fhBrotherRoom_vacation_evening: {
honest: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's on his bed, lamp on. Read earlier, put the book down, didn't pick anything else up. He looks okay.<</narrative>>
<<dialog "brother">>(when you appear) Come in for a sec.
(moving so there's room on the end of the bed)
I wanted to say something.<</dialog>>
<<dialog "player">>(sitting) What?<</dialog>>
<<dialog "brother">>This vacation's been good. Like, actually good.
(looking at his hands briefly)
A lot of that's because of you.
(pre-empting your reaction)
Don't make it a whole thing.<</dialog>>`,
friendship: 4, love: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He says something honest without hiding it behind a joke first. A sign of how much things have changed.<</narrative>>
<<dialog "brother">>I was kind of a mess at the start of the year. When we moved.
(factual, not dramatic)
I think I took it out on everyone.
(looking at you)
Including you. So. Sorry for that.<</dialog>>
<<dialog "player">>You were adjusting.<</dialog>>
<<dialog "brother">>(quiet)
Still. I know I was difficult.
(back to normal, quickly)
Okay. That's it. I said it. Done.<</dialog>>`,
friendship: 4, trust: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He asks you to sit down properly. He pulls the spare chair out from under his desk.<</narrative>>
<<dialog "brother">>I want to ask you something and I want a real answer, not just a nice one.
(sitting on his bed, serious)
Do you think I'm actually talented? At the design stuff. Or is it just... okay for a teenager?<</dialog>>
<<dialog "player">>(honest answer)<</dialog>>
<<dialog "brother">>(absorbing it properly, not deflecting)
(nodding slowly)
Okay. Thank you. That's what I needed to hear.<</dialog>>`,
friendship: 4, trust: 3
}
],
late: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's drawing late. His desk lamp the only light, the house quiet around him.<</narrative>>
<<dialog "player">>(at the door, quietly) Still up?<</dialog>>
<<dialog "brother">>(not startled, expected you) Yeah. I'm in the middle of something.
(not closing the sketchbook)
You can look if you want.<</dialog>>
<<dialog "player">>(looking over his shoulder at what he's drawing)<</dialog>>
<<dialog "brother">>(carrying on drawing)
It's for the game. The world map.<</dialog>>`,
friendship: 4, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's been in here for hours and is clearly not sleeping soon. He looks up when you come in.<</narrative>>
<<dialog "brother">>You're up late.<</dialog>>
<<dialog "player">>So are you.<</dialog>>
<<dialog "brother">>(fair point) I'm building something. It's different.
(a beat)
Come in if you want. I'll talk while I work.<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's about to stop for the night and he looks up at you with the tired-but-good look of someone who made something today.<</narrative>>
<<dialog "brother">>Good night. I mean actually. It was a good night. In here.
(patting the sketchbook)
Did a lot.<</dialog>>
<<dialog "player">>Yeah?<</dialog>>
<<dialog "brother">>(quietly content) Yeah. Night.
(and means it)<</dialog>>`,
friendship: 4, love: 1
}
],
caring: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He says goodnight from the hallway. Proper, warm, not rushed.<</narrative>>
<<dialog "brother">>Hey. Night.
(making eye contact, not just calling from behind the door)
You okay?<</dialog>>
<<dialog "player">>Yeah. You?<</dialog>>
<<dialog "brother">>(genuinely) Yeah. Really good today.
(going into his room)
Night.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He notices you're tired before you say anything.<</narrative>>
<<dialog "brother">>(looking at you) You look tired.<</dialog>>
<<dialog "player">>Bit.<</dialog>>
<<dialog "brother">>(standing up from his desk) Go to sleep. I'll be quiet.
(and then actually turns his music down without you asking)<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/bedroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He left something outside your door at some point in the evening. A snack. A note. Something small and deliberate. When you mention it he shrugs.<</narrative>>
<<dialog "brother">>I had extra.
(very firmly not making it a big thing)
That's all it is.<</dialog>>
<<dialog "player">>Jake.<</dialog>>
<<dialog "brother">>(pre-empting) Don't.<</dialog>>
<<dialog "player">>Thank you.<</dialog>>
<<dialog "brother">>(quiet for a moment, then going back to his desk)
...Yeah.<</dialog>>`,
friendship: 4, love: 3
}
]
},
fhKitchen_vacation_morning: {
morning: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He made breakfast. Actual breakfast. Eggs and toast, two plates, without being asked.<</narrative>>
<<dialog "brother">>(when you come in) I was making it anyway. Sit down.
(putting a plate in front of you, casual about it)<</dialog>>
<<dialog "player">>Thanks, Jake.<</dialog>>
<<dialog "brother">>(already eating his own) Don't thank me. I had extra eggs.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's at the table with good morning energy. Rare, and slightly funny.<</narrative>>
<<dialog "brother">>(looking up) Morning. I slept properly for once. Eight hours.
(with the wonder of someone who discovered it's possible)
I feel like a different person.<</dialog>>
<<dialog "player">>You look human.<</dialog>>
<<dialog "brother">>(pointing at you) There it is.
(eating, grinning slightly)<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He makes room at the table for you without any ceremony. Just moves his stuff over.<</narrative>>
<<dialog "brother">>(sliding across the juice) Sit. It's a good morning.
(looking out the window)
I don't know why. It just is.<</dialog>>`,
friendship: 4, love: 1
}
],
confession: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He says it while looking at his tea. The mug in his hands, not at you:<</narrative>>
<<dialog "brother">>I drew something for you. Like, specifically for you. Not to show you. Just made it.
(a pause)
It's of a thing you said once. About somewhere you wanted to go.
(quickly)
I don't know if I'll give it to you. I haven't decided.<</dialog>>`,
friendship: 4, love: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He says something he's never said out loud before, and means every word of it:<</narrative>>
<<dialog "brother">>I draw every day now. Started properly this vacation.
(looking down at the table)
I think it might be the thing I'm actually good at.
(a beat, honest)
I think it might be the first thing I've genuinely wanted to get better at.<</dialog>>`,
friendship: 4, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He says it simply:<</narrative>>
<<dialog "brother">>You're the first person I showed the game design stuff to.
(drinking his tea)
I wanted you to see it before anyone else. I don't know why. It felt right.
(shrugging, not requiring a response)<</dialog>>`,
friendship: 4, trust: 3, love: 1
}
],
easy: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He passes you the butter before you reach for it. Knows what you take in your toast. Small things.<</narrative>>
<<dialog "player">>(noticing) Thanks.<</dialog>>
<<dialog "brother">>(already on his own breakfast) You always have butter. You've had butter every morning for like two weeks. I noticed.
(completely matter-of-fact)<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's made both your drinks before you got there. Knows how you take yours.<</narrative>>
<<dialog "brother">>(when you come in) It's there.
(pointing to the counter, back to the window)
I've been watching the garden. A fox came through at like six AM. Proper fox.<</dialog>>
<<dialog "player">>Were you up at six?<</dialog>>
<<dialog "brother">>(peaceful) I stayed up until six. Different thing.<</dialog>>`,
friendship: 4, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He passes you something mid-sentence, whatever you needed, then keeps talking like it didn't happen. Just easy.<</narrative>>
<<dialog "brother">>(handing it across, continuing his story) and then the guy online says, no that's not a bug that's a feature, and I'm like
(you take it)
and I'm like, it clearly IS a bug because<</dialog>>
<<narrative>>He's still talking. You're both at the table. It's the easiest morning you've had in a while.<</narrative>>`,
friendship: 4, love: 2
}
]
},
fhKitchen_vacation_evening: {
dinner: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He sets the table. Both sides. Properly.<</narrative>>
<<dialog "player">>(watching) When did you start doing that?<</dialog>>
<<dialog "brother">>(putting down glasses) I always did it.
(pause)
I do it now. It's a new thing.
(sitting down)
Don't make a thing of it.<</dialog>>`,
friendship: 4, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's telling you about something at dinner and he's properly animated. The kind of story that has gestures.<</narrative>>
<<dialog "brother">>Okay so the level I was designing, the whole ecosystem thing, I figured out how the food chain works. I worked it out on paper. It's like six pages.
(spreading his hands)
I know that sounds like nothing but it was really hard to<</dialog>>
<<dialog "player">>Show me after?<</dialog>>
<<dialog "brother">>(stopping mid-word, delighted) Yeah. Yeah, definitely.<</dialog>>`,
friendship: 4, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He serves you first when he's dishing up. Just does it. Doesn't comment.<</narrative>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "brother">>(sitting) You always forget to serve yourself when you're talking. I've noticed.
(completely calm about having noticed this)<</dialog>>`,
friendship: 4, love: 2
}
],
late: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's in the kitchen late and when you come down he immediately makes room.<</narrative>>
<<dialog "brother">>(moving over) Hey. Can't sleep either?<</dialog>>
<<dialog "player">>Not really.<</dialog>>
<<dialog "brother">>(nodding) I made tea. There's more water in the kettle.
(sitting back with his mug)
Come sit. I wasn't going anywhere anyway.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He says something late that feels like the beginning of a long conversation:<</narrative>>
<<dialog "brother">>Do you ever think about what everything will look like in ten years? Like, the family. The house. All of it?<</dialog>>
<<dialog "player">>Sometimes. You?<</dialog>>
<<dialog "brother">>(looking at his mug) I think about it more now than I used to.
(quietly)
I think I used to not want to think about it.<</dialog>>`,
friendship: 4, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>It's past midnight and he's made both of you something to eat without being asked. You didn't say you were hungry.<</narrative>>
<<dialog "brother">>(sliding a bowl across) You had that look.
(sitting down with his own bowl)<</dialog>>
<<dialog "player">>What look?<</dialog>>
<<dialog "brother">>(eating) The hungry-but-won't-say-it look. I know it.
(and just like that, it's easy again)<</dialog>>`,
friendship: 4, love: 3
}
],
sharing: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He puts the sketchbook on the table between you, open, at the beginning, and waits.<</narrative>>
<<dialog "brother">>You can look through it. All of it. I don't mind anymore.
(watching your hands, not your face)
Tell me if any of it's bad. Honestly.<</dialog>>`,
friendship: 4, trust: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He explains the game concept fully. The whole thing, not the sanitised version he gives everyone else.<</narrative>>
<<dialog "brother">>Okay. The full version. Ready?
(and then he tells you everything. The world, the characters, the mechanics, the ending)
...That's it. That's the whole thing.
(looking at you, vulnerable)
What do you think?<</dialog>>`,
friendship: 4, trust: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He shows you a drawing he did of the family. You're in it. He hadn't planned to show you this one.<</narrative>>
<<dialog "brother">>(seeing you see it, quickly) That one was just practice. Figures and stuff.
(not taking it away)
I draw what's around me. You were. It's not a thing.<</dialog>>
<<dialog "player">>Jake. It's really good.<</dialog>>
<<dialog "brother">>(very quietly) I know.
(closing the sketchbook gently)
Thanks.<</dialog>>`,
friendship: 4, love: 3, trust: 2
}
]
},
fhLivingroom_vacation_morning: {
present: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's downstairs early. Sketchbook on his lap, back door slightly open for air. Comfortable.<</narrative>>
<<dialog "player">>(coming down) You're down here again.<</dialog>>
<<dialog "brother">>(not looking up) It's better in the morning. The light's different.
(turning a page)
There's tea in the pot. I kept it warm.<</dialog>>`,
friendship: 4, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's made himself at home in the living room. Morning newspaper someone left, a cup of tea, the sofa configured for a morning stay.<</narrative>>
<<dialog "brother">>(glancing up) There's a whole article in here about game design. I pulled it out.
(holding up a torn page)
Don't tell Dad I tore it. I'll recycle it after.<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's just sitting. Sketchbook closed, tea nearby. You come in and he shifts over without being asked.<</narrative>>
<<dialog "brother">>(when you sit) Good morning.
(back to looking at nothing, content)
It's a good one today.<</dialog>>`,
friendship: 4, love: 1
}
],
quiet: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>You're both doing your own things. Parallel morning. He glances over occasionally with nothing to say, and that's enough.<</narrative>>
<<dialog "brother">>(eventually) This is good.
(meaning: this, right now, the two of you just existing)<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "brother">>(back to his sketchbook)<</dialog>>`,
friendship: 4, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's drawing, you're doing something nearby. He occasionally narrates what he's working on without looking up. Half to you, half to himself.<</narrative>>
<<dialog "brother">>(sketching) I'm trying to do hands. Hands are the worst.
(a beat)
Actually no, feet are the worst. But hands are close.<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He shows you something in the sketchbook without making it a presentation. Just holds it up as you pass.<</narrative>>
<<dialog "brother">>Tell me if the proportions are off.
(holds up the page)
I've been looking at it too long. I can't tell anymore.<</dialog>>`,
friendship: 4, trust: 2
}
],
talking: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He sits across from you and asks something genuine:<</narrative>>
<<dialog "brother">>What do you actually think of me? Like, honestly. As a person.
(before you can answer)
Not nice. Honest.<</dialog>>
<<dialog "player">>(taking it seriously, answering honestly)<</dialog>>
<<dialog "brother">>(listening to the whole thing)
(quiet after)
Okay.
(sitting back)
That's... actually pretty good.<</dialog>>`,
friendship: 4, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He brings something up that he's been sitting on for a while:<</narrative>>
<<dialog "brother">>I want to apply somewhere after school. Like a proper design course.
(not looking at you)
I haven't told Mom and Dad yet. I don't know how they'll take it.
(a beat)
What do you think I should say to them?<</dialog>>`,
friendship: 4, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He says something that costs him but he says it anyway:<</narrative>>
<<dialog "brother">>I think you're the person in this house who actually gets me.
(not dramatic. Just observational)
Dad tries but he doesn't really. Mom does in her way.
(looking at you)
You just sort of... do. Without trying.<</dialog>>`,
friendship: 4, trust: 3, love: 3
}
]
},
fhLivingroom_vacation_afternoon: {
together: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He shows you the game document on his phone. The actual file, the organised version, the one he's been building for months.<</narrative>>
<<dialog "brother">>(handing you the phone) Don't scroll too fast. The structure matters.
(nervous in the way only something real can make you nervous)
Tell me what you think of the first level.<</dialog>>`,
friendship: 4, trust: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's found something you'd like and put it on before you got there.<</narrative>>
<<dialog "brother">>(when you come in) I found this one. I think you'll actually like it.
(completely certain about this)
Sit down. It starts slow but stay with it.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's settled on the sofa and reaches across to pass you the snacks when you sit. Automatic now. Easy.<</narrative>>
<<dialog "brother">>(not looking away from the screen) Good afternoon.
(completely deadpan)
Nice of you to join me.<</dialog>>
<<dialog "player">>It's my house too.<</dialog>>
<<dialog "brother">>(taking some crisps) Technically. Technically your house too.<</dialog>>`,
friendship: 4, love: 2
}
],
honest: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He turns off the TV. Just turns it off, middle of nothing, to say something:<</narrative>>
<<dialog "brother">>I'm nervous about going back to school. Still. Even after everything.
(matter of fact)
I thought it would go away but it hasn't. I'm just better at managing it now.
(pause)
Is that normal for it to stay?<</dialog>>`,
friendship: 4, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He admits something he'd never have said earlier:<</narrative>>
<<dialog "brother">>At the start of the year I wanted to go back to the old house so badly. Like, badly.
(looking at the room)
Now I don't. I think this one's actually better.
(shrugging)
Funny how that works.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He says something plainly and then sits with it:<</narrative>>
<<dialog "brother">>I think this was the best vacation I've had. I know that's a low bar.
(looking at the window)
But it actually is. I felt like myself.
(quietly)
I don't always feel like myself.<</dialog>>`,
friendship: 4, trust: 3, love: 2
}
],
goofy: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He does an impression so good you both lose it completely. The particular helpless laughter of people who know the same person.<</narrative>>
<<dialog "brother">>(doing Dad) "Jake, have you thought about your future?"
(switching)
"Jake, are you eating enough?"
(switching again)
"Jake, what are your five-year plans?"
(completely losing his own composure)<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He challenges you to something stupid and is completely committed to winning.<</narrative>>
<<dialog "brother">>Trivia. Right now. Best of three. I've been practising geography.
(getting his phone out)
Actually I've been accidentally practising geography because of the bear fact thing. But still. Ready?<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He says something absurd with complete sincerity and it sets you both off. There's an ease to it now that wasn't there before.<</narrative>>
<<dialog "brother">>(very serious) I've been thinking about this and I genuinely believe I could win a reality TV show. The ones where you have to like build things and be strategic.
(completely certain)
I have a specific skill set.<</dialog>>
<<dialog "player">>You got stressed when the wifi went down last week.<</dialog>>
<<dialog "brother">>(long pause) That was different. That was structural pressure. Reality TV is competitive pressure. Different adrenaline.<</dialog>>`,
friendship: 4, love: 2
}
]
},
fhLivingroom_vacation_evening: {
filmNight: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's set everything up. Good blankets, actual snacks, the film you mentioned once already queued.<</narrative>>
<<dialog "brother">>(when you come in) You said you wanted to watch this one. Three days ago. I remembered.
(completely casual about having remembered)
Sit down. I'll get more drinks.<</dialog>>`,
friendship: 4, love: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He passes you something from the snack bowl and doesn't take his eyes off the screen. It's completely automatic. Normal.<</narrative>>
<<dialog "player">>(taking it) Thanks.<</dialog>>
<<dialog "brother">>(watching the screen) You were going to want that part. Keep watching.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's set up next to you. Close enough that it's comfortable, far enough that it's not a thing. Just right.<</narrative>>
<<dialog "brother">>(pressing play) Good choice tonight. I admit it.
(settling in)
You have better taste than I said you had.<</dialog>>
<<dialog "player">>You said my taste was objectively bad.<</dialog>>
<<dialog "brother">>(watching the screen) That was a different conversation. I've revised my position.<</dialog>>`,
friendship: 4, love: 2
}
],
end: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>The film finishes and neither of you moves. He stares at the dark screen for a moment.<</narrative>>
<<dialog "brother">>(quietly) That was actually really good.
(not moving)
I didn't expect that.<</dialog>>
<<dialog "player">>I told you.<</dialog>>
<<dialog "brother">>(small exhale, almost a laugh) Yeah.
(staying put)
I know. You did.<</dialog>>`,
friendship: 4, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>Credits roll. He doesn't skip them. He looks at you instead.<</narrative>>
<<dialog "brother">>That character at the end. The way he
(stops himself)
Never mind. You know what I mean.<</dialog>>
<<dialog "player">>Yeah. I know.<</dialog>>
<<dialog "brother">>(nodding, satisfied)
Good.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He says something after the film that's real and small and true:<</narrative>>
<<dialog "brother">>(to the dark screen) This is a good way to spend an evening.
(a beat)
With you. Specifically with you.
(pre-empting his own embarrassment)
Don't write it down.<</dialog>>`,
friendship: 4, love: 3
}
],
deep: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>The TV is off. The lamp is low. He says something he means completely:<</narrative>>
<<dialog "brother">>I used to think I was going to be the kind of person who didn't need anyone. Like, independent to the point of not needing people.
(looking at his hands)
I don't think that anymore.
(quiet)
I think I was just scared of needing people.<</dialog>>`,
friendship: 4, trust: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He sits in the dim room and says:<</narrative>>
<<dialog "brother">>You know what I think is weird? You came into my life as a sister and you actually are one. Like genuinely are one.
(not looking at you)
I didn't know that was something that could happen. That fast.
(quickly)
Anyway. Night.<</dialog>>
<<narrative>>He goes upstairs before you can say anything. You hear his door close, gently.<</narrative>>`,
friendship: 4, trust: 3, love: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He asks something quietly in the dark room:<</narrative>>
<<dialog "brother">>Do you think we'll stay close? When everything changes? School ending, people moving, all of it?<</dialog>>
<<dialog "player">>Yeah. I do.<</dialog>>
<<dialog "brother">>(sitting with that for a long moment)
(nodding)
Me too.
(a breath)
Good.<</dialog>>`,
friendship: 4, trust: 3, love: 3
}
]
},
fhBackyard_vacation_afternoon: {
outside: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's in the garden with his sketchbook. Actively chose to come outside with it.<</narrative>>
<<dialog "player">>(coming out) Hey.<</dialog>>
<<dialog "brother">>(not looking up from the page) Hey.
(a beat)
The light's good today. For drawing.
(showing you what he's working on without being asked)
I'm trying to do the garden. The actual garden.<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's found a spot that works for him. The same corner he always ends up in now. He looks up when you come out.<</narrative>>
<<dialog "brother">>(making room) Sit here. The sun hits this spot right.
(moving over, easy about it)<</dialog>>
<<dialog "player">>Since when is this your spot?<</dialog>>
<<dialog "brother">>(already back to his sketchbook) Since I claimed it. Laws of possession. It's mine.<</dialog>>`,
friendship: 4, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's on the step with a cold drink and his notebook, looking out at the garden.<</narrative>>
<<dialog "brother">>(when you come out) Good timing. I wanted to run something past you.
(opening the notebook)
The last world in the game. I can't figure out the mood of it. What would you make it feel like?<</dialog>>`,
friendship: 4, trust: 2
}
],
mellow: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He shows you his best drawing from the vacation. Holds it up in the afternoon light.<</narrative>>
<<dialog "brother">>(holding it out) Best thing I've done.
(watching your face as you look)
I'm not being modest. It actually is.<</dialog>>
<<dialog "player">>(genuinely) It's really good.<</dialog>>
<<dialog "brother">>(putting it carefully back in the sketchbook) Yeah. I know.
(pleased in a quiet way)<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's leaning back with his face in the sun. No phone, no sketchbook, just there.<</narrative>>
<<dialog "brother">>(eyes closed) This is a good afternoon.
(very settled)
Tell me something.<</dialog>>
<<dialog "player">>What?<</dialog>>
<<dialog "brother">>(still eyes closed) Anything. I just want background noise that's interesting.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He asks you something while looking at the garden:<</narrative>>
<<dialog "brother">>If you could draw something. Anything, just for yourself, no one else. What would it be?<</dialog>>
<<dialog "player">>(thinking, answering)<</dialog>>
<<dialog "brother">>(nodding, considering this seriously) I could try to draw that.
(opening his sketchbook)
If you want. Just as a thing. No reason.<</dialog>>`,
friendship: 4, love: 2
}
],
real: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He says it simply, like he's been deciding when to say it:<</narrative>>
<<dialog "brother">>If something went badly wrong, like properly wrong, you'd be the person I'd call first.
(not looking at you)
Not Mom, not Dad. You.
(a beat)
That's probably a weird thing to say.<</dialog>>
<<dialog "player">>It's not weird.<</dialog>>
<<dialog "brother">>(quietly)
Good.
(back to looking at the garden)<</dialog>>`,
friendship: 4, trust: 3, love: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He names it directly for the first time:<</narrative>>
<<dialog "brother">>I didn't think I'd like having an older sister. I thought it would be weird.
(looking at you sideways)
It's not weird. It's actually good.
(goes back to his sketchbook immediately)
Okay. Moving on. What do you want for dinner.<</dialog>>`,
friendship: 4, trust: 3, love: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's in a real mood. Expansive, open. The version of him that comes out in good light on good days.<</narrative>>
<<dialog "brother">>I'm going to be okay.
(not fishing, just stating it)
Like, I genuinely think I'm going to be okay. The school stuff, the future stuff.
(glancing at you)
I didn't always think that.<</dialog>>
<<dialog "player">>I know.<</dialog>>
<<dialog "brother">>(nodding)
Yeah. I know you know.<</dialog>>`,
friendship: 4, trust: 3, love: 2
}
]
},
fhBackyard_vacation_evening: {
sunset: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's in his corner when you come out. Moves automatically to let you in.<</narrative>>
<<dialog "brother">>(making room) Sit. It's about to get good.
(watching the sky)<</dialog>>
<<dialog "player">>(sitting beside him) What are we watching?<</dialog>>
<<dialog "brother">>The light goes orange in about five minutes. Every evening this week. I've been tracking it.
(small smile)
It's good.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's been sketching the sunset. He shows you the page without you asking.<</narrative>>
<<dialog "brother">>(holding it up against the actual sky) It's not as good as the real one but
(looking between them)
The colour's close. I mixed three things to get that orange.<</dialog>>`,
friendship: 4, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's been out here a while when you come out. He says:<</narrative>>
<<dialog "brother">>I've been thinking about what I want to do differently next year.
(to the sky)
More of this. Outside. Drawing. Talking to people who are worth talking to.
(a beat)
Less of just... surviving.<</dialog>>`,
friendship: 4, trust: 3, love: 2
}
],
stars: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's on his back in the grass and points up when you come out.<</narrative>>
<<dialog "brother">>(pointing) Okay. That cluster up there. Three stars in a triangle. That's Triangulum.
(a pause)
I learned that from a video. I've been watching videos.
(slightly embarrassed)
It's genuinely interesting though.<</dialog>>`,
friendship: 4
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He lies in the grass next to you and narrates quietly. Things he's looked up. Things he noticed. Things he thought while you weren't here.<</narrative>>
<<dialog "brother">>(pointing at a star) That one's been there every night this week. Same spot.
(a long pause)
I never noticed stars before this vacation.
(simply)
Now I do.<</dialog>>`,
friendship: 4, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He says something out of nowhere, looking up:<</narrative>>
<<dialog "brother">>I want to draw the sky. Like, really draw it. Properly.
(a pause)
I don't think I'm good enough yet.
(another pause)
I will be though.<</dialog>>
<<dialog "player">>I know you will.<</dialog>>
<<dialog "brother">>(quietly, looking at the stars)
Yeah. I think so too.<</dialog>>`,
friendship: 4, trust: 2, love: 1
}
],
closing: [
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He goes inside and then turns back in the doorway. Warm light behind him, garden dark now.<</narrative>>
<<dialog "brother">>Hey.
(just looking at you for a second)
Glad you're my sister.
(going inside immediately, door half-closed)
Night.<</dialog>>
<<narrative>>The door closes. You're in the garden alone. The porch light is still on.<</narrative>>`,
friendship: 4, love: 4, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's at the back door and says:<</narrative>>
<<dialog "brother">>This was a good vacation.
(looking back at the garden)
I'll remember it.
(and he means that. The sky, the drawings, the conversations, all of it)
Come in when you're ready. I'll leave the light on.<</dialog>>`,
friendship: 4, love: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/brother/vacation/level2/backyardEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He stands in the doorway for a moment, silhouette against the kitchen light, and says something simple:<</narrative>>
<<dialog "brother">>Thanks.
(not specifying for what)
For being around.
(going in, quietly)<</dialog>>
<<narrative>>The door swings half-shut. Through the window you can see him put the kettle on. Making tea for both of you without asking, one last time.<</narrative>>`,
friendship: 4, love: 4, trust: 3
}
]
}
}>>/* Global character generator. Usage: <<charGenerate dmId>>
dmId: Fotogram DM id (looks up dm for context, skinTone, caches result in dm.generatedPromotedCharId)
Result: State.temporary._generatedCharId */
<<widget "charGenerate">><<nobr>>
<<set _dmId = $args[0]>>
<<if !_dmId>><<return>><</if>>
<<run (function(dmId){
if (!dmId) return;
var vars = State.variables;
var dms = vars.phoneFotogramDMs || [];
var dm = null;
for (var i = 0; i < dms.length; i++) { if (dms[i].id === dmId) { dm = dms[i]; break; } }
if (!dm) return;
if (dm.generatedPromotedCharId) {
State.temporary["_generatedCharId"] = dm.generatedPromotedCharId;
return;
}
function pick(arr, fallback) {
return (Array.isArray(arr) && arr.length) ? arr[Math.floor(Math.random() * arr.length)] : fallback;
}
function weightedPick(arr, fallback) {
if (!Array.isArray(arr) || arr.length === 0) return fallback;
var total = 0;
for (var wi = 0; wi < arr.length; wi++) {
var w = Number(arr[wi] && arr[wi].weight);
if (Number.isFinite(w) && w > 0) total += w;
}
if (total <= 0) return pick(arr, fallback);
var roll = Math.random() * total;
var acc = 0;
for (var wj = 0; wj < arr.length; wj++) {
var item = arr[wj];
var ww = Number(item && item.weight);
if (!Number.isFinite(ww) || ww <= 0) continue;
acc += ww;
if (roll <= acc) return item;
}
return arr[arr.length - 1] || fallback;
}
function rint(min, max) {
var lo = Number(min), hi = Number(max);
if (!Number.isFinite(lo)) lo = 0;
if (!Number.isFinite(hi)) hi = lo;
if (hi < lo) { var t = hi; hi = lo; lo = t; }
return lo + Math.floor(Math.random() * (hi - lo + 1));
}
var cfg = (typeof setup !== "undefined" && setup.charGenerator) ? setup.charGenerator : {};
var initState = (cfg.initialState && typeof cfg.initialState === "object") ? cfg.initialState : {};
var maleW = Number(cfg.maleWeight != null ? cfg.maleWeight : 0.5);
var femaleW = Number(cfg.femaleWeight != null ? cfg.femaleWeight : 0.5);
if (!Number.isFinite(maleW) || maleW < 0) maleW = 0.5;
if (!Number.isFinite(femaleW) || femaleW < 0) femaleW = 0.5;
var totalW = maleW + femaleW;
var dmGender = String(dm.profileGender || "").toLowerCase();
var gender = (dmGender === "male" || dmGender === "female")
? dmGender
: ((totalW <= 0) ? (Math.random() < 0.5 ? "male" : "female") : (Math.random() < (maleW / totalW) ? "male" : "female"));
var maleNames = cfg.maleFirstNames || ["Luca", "Noah", "Ethan", "Leo", "Mason"];
var femaleNames = cfg.femaleFirstNames || ["Aria", "Mila", "Nora", "Sofia", "Lena"];
var unisexNames = cfg.unisexFirstNames || ["Alex", "Sam", "Riley", "Taylor"];
var lastNames = cfg.lastNames || ["Carter", "Hayes", "Brooks", "Reed", "Morgan"];
var ethnicities = cfg.ethnicities || [];
var firstPool = gender === "male" ? maleNames : femaleNames;
var firstName = pick(firstPool, pick(unisexNames, "Alex"));
var lastName = pick(lastNames, "Taylor");
var ethnicity = weightedPick(ethnicities, null);
var ethnicityId = ethnicity && ethnicity.id ? ethnicity.id : "unspecified";
var ethnicityLabel = ethnicity && ethnicity.label ? ethnicity.label : "Unspecified";
var tones = (typeof setup !== "undefined" && Array.isArray(setup.fotogramAnonSkinTones) && setup.fotogramAnonSkinTones.length)
? setup.fotogramAnonSkinTones
: ["white", "black", "tan"];
if (Array.isArray(cfg.skinTones) && cfg.skinTones.length) tones = cfg.skinTones;
var preferredTones = (ethnicity && Array.isArray(ethnicity.preferredSkinTones) && ethnicity.preferredSkinTones.length)
? ethnicity.preferredSkinTones
: tones;
var skinTone = dm.skinTone || pick(preferredTones, pick(tones, "tan"));
var hRanges = cfg.heightRanges || { male: { min: 168, max: 198 }, female: { min: 150, max: 182 } };
var hRange = hRanges[gender] || { min: 155, max: 195 };
var heightCm = rint(hRange.min, hRange.max);
var currentYear = (vars.timeSys && Number(vars.timeSys.year)) || 2024;
var ageMin = Number(cfg.ageMin);
var ageMax = Number(cfg.ageMax);
if (!Number.isFinite(ageMin)) ageMin = 18;
if (!Number.isFinite(ageMax)) ageMax = 60;
if (ageMax < ageMin) { var at = ageMax; ageMax = ageMin; ageMin = at; }
var dmAge = Number(dm.profileAge);
var birthYear = (Number.isFinite(dmAge) && dmAge >= ageMin && dmAge <= ageMax)
? (currentYear - dmAge)
: rint(currentYear - ageMax, currentYear - ageMin);
var avatarPool = cfg.avatarPool || { male: { white: [], black: [], tan: [] }, female: { white: [], black: [], tan: [] } };
var gp = avatarPool[gender] || {};
var avatar = "";
var tonePool = Array.isArray(gp[skinTone]) ? gp[skinTone] : [];
if (tonePool.length > 0) {
avatar = pick(tonePool, "");
} else {
for (var tp = 0; tp < tones.length; tp++) {
var toneKey = tones[tp];
var altPool = Array.isArray(gp[toneKey]) ? gp[toneKey] : [];
if (altPool.length > 0) {
skinTone = toneKey;
avatar = pick(altPool, "");
break;
}
}
}
var occupations = Array.isArray(cfg.occupations) && cfg.occupations.length ? cfg.occupations : ["Student", "Barista", "Retail Worker", "Freelancer", "Office Assistant"];
var statuses = Array.isArray(cfg.statuses) && cfg.statuses.length ? cfg.statuses : ["Online Contact", "DM Contact", "New Contact"];
var colors = Array.isArray(cfg.colors) && cfg.colors.length ? cfg.colors : ["#a855f7", "#3b82f6", "#f97316", "#14b8a6", "#ec4899"];
var body;
if (gender === "male") {
var mb = cfg.maleBody || { penisMinCm: 10, penisMaxCm: 22 };
var penisCm = rint(mb.penisMinCm, mb.penisMaxCm);
body = { penisCm: penisCm, penisTier: penisCm >= 19 ? "large" : (penisCm >= 15 ? "medium" : "small") };
} else {
var fb = cfg.femaleBody || { bustMinCm: 78, bustMaxCm: 108, waistMinCm: 56, waistMaxCm: 84, hipsMinCm: 82, hipsMaxCm: 120, hairMinCm: 8, hairMaxCm: 90 };
var bust = rint(fb.bustMinCm, fb.bustMaxCm);
var waist = rint(fb.waistMinCm, fb.waistMaxCm);
var hips = rint(fb.hipsMinCm, fb.hipsMaxCm);
body = {
bustCm: bust,
waistCm: waist,
hipsCm: hips,
hairLengthCm: rint(fb.hairMinCm, fb.hairMaxCm),
waistHipRatio: hips > 0 ? Number((waist / hips).toFixed(2)) : null,
bustWaistRatio: waist > 0 ? Number((bust / waist).toFixed(2)) : null
};
}
var baseId = (firstName + "_" + lastName).toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
if (!baseId) baseId = "generated_contact";
var generatedCharId = baseId;
var idCounter = 2;
var defs = (typeof setup !== "undefined" && setup.characterDefs) ? setup.characterDefs : {};
while ((defs && defs[generatedCharId]) || (vars.characters && vars.characters[generatedCharId])) {
generatedCharId = baseId + "_" + idCounter;
idCounter++;
}
var fullName = firstName + " " + lastName;
var occupation = pick(occupations, "Unknown");
var status = pick(statuses, "Contact");
var color = pick(colors, "#a855f7");
var infoText = "<p>You met this person online and swapped numbers through DMs.</p>";
var def = {
id: generatedCharId,
name: fullName,
firstName: firstName,
lastName: lastName,
birthYear: birthYear,
occupation: occupation,
location: "Online",
avatar: avatar,
type: "npc",
color: color,
status: status,
gender: gender,
ethnicity: ethnicityId,
ethnicityLabel: ethnicityLabel,
skinTone: skinTone,
heightCm: heightCm,
body: body,
hasSchedule: false,
scheduleType: "none",
generatedFromPhone: true,
info: infoText
};
if (!vars.phoneGeneratedContacts) vars.phoneGeneratedContacts = {};
vars.phoneGeneratedContacts[generatedCharId] = def;
if (!vars.characters) vars.characters = {};
if (!vars.characters[generatedCharId]) {
var initStats = (initState.stats && typeof initState.stats === "object")
? {
love: Number(initState.stats.love || 0),
friendship: Number(initState.stats.friendship || 0),
lust: Number(initState.stats.lust || 0),
trust: Number(initState.stats.trust || 0)
}
: { love: 0, friendship: 25, lust: 0, trust: 5 };
var initOpinion = (initState.opinion && typeof initState.opinion === "object")
? {
awareness: Number(initState.opinion.awareness || 0),
flags: Array.isArray(initState.opinion.flags) ? initState.opinion.flags.slice() : []
}
: { awareness: 0, flags: [] };
vars.characters[generatedCharId] = {
stats: initStats,
opinion: initOpinion,
firstMet: (initState.firstMet != null) ? initState.firstMet : null,
known: (initState.known != null) ? !!initState.known : true,
currentLocation: (initState.currentLocation != null) ? initState.currentLocation : null,
currentStatus: (initState.currentStatus != null) ? initState.currentStatus : null
};
}
if (typeof setup !== "undefined") {
if (!setup.characterDefs) setup.characterDefs = {};
setup.characterDefs[generatedCharId] = def;
if (!Array.isArray(setup.relationPlaces)) setup.relationPlaces = [];
var relCfg = cfg.relations || {};
var relLocation = relCfg.locationId || "maplewood";
var relPlaceId = relCfg.placeId || "onlineContacts";
var relPlaceName = relCfg.placeName || "Online Contacts";
var relPlace = null;
for (var rp = 0; rp < setup.relationPlaces.length; rp++) {
var place = setup.relationPlaces[rp];
if (place && place.locationId === relLocation && place.placeId === relPlaceId) { relPlace = place; break; }
}
if (!relPlace) {
relPlace = { locationId: relLocation, placeId: relPlaceId, name: relPlaceName, members: [] };
setup.relationPlaces.push(relPlace);
}
if (!Array.isArray(relPlace.members)) relPlace.members = [];
if (relPlace.members.indexOf(generatedCharId) === -1) relPlace.members.push(generatedCharId);
}
dm.generatedPromotedCharId = generatedCharId;
State.temporary["_generatedCharId"] = generatedCharId;
})($args[0])>>
<</nobr>><</widget>><<nobr>>
/* Save entry passage only when coming from a location (not from Talk/Meetup/PhoneSwap sub-passages) so Leave returns to the right place */
<<set _prev = previous()>>
<<set _fromSubPassage = _prev && (_prev.includes("Talk") || _prev.includes("Meetup") || _prev.startsWith("PhoneSwap_"))>>
<<unset $phoneSwapTargetCharId>>
<<if _prev && !_fromSubPassage>>
<<set $interactionEntryPassage = _prev>>
<</if>>
/* When coming from Talk or same-location character flows (Back): use $location, fallback to saved entry (set when we first entered) */
<<if _prev && (_prev.includes("Talk") || _prev.includes("parkRunnerLilyJog"))>>
<<set _returnLoc = $location || $interactionEntryPassage>>
<<else>>
<<set _returnLoc = $interactionEntryPassage || _prev || $location>>
<</if>>
<<if !_returnLoc>>
<<set _returnLoc = $interactionEntryPassage || $location>>
<</if>>
<<if !$interactingChar || !$characters[$interactingChar]>>
/* Clear the saved entry passage before leaving */
<<unset $interactionEntryPassage>>
<<goto _returnLoc>>
<</if>>
<<if def "touchCharacterRelation">>
<<touchCharacterRelation $interactingChar>>
<</if>>
<<set _charState = $characters[$interactingChar]>>
<<set _char = setup.getCharacter($interactingChar)>>
/* Make sure character locations are updated */
<<if !_charState.currentLocation>>
<<updateCharacterLocations>>
<<set _charState = $characters[$interactingChar]>>
<</if>>
<<set _charLoc = _charState.currentLocation || "unknown">>
<<set _status = _charState.currentStatus || "available">>
<<set $location = _returnLoc>>
/* Get display name from navCards, fallback to raw ID */
<<set _charLocName = setup.navCards[_returnLoc]?.name || _returnLoc>>
<<if _status === "sleeping">>
<<narrative _charLocName>>
You quietly approach <<print _char.firstName>>'s room. The door is slightly ajar, and you can hear soft breathing. They're fast asleep.
<</narrative>>
<div class="location-actions">
<<btn "Leave quietly" _returnLoc "secondary">><<unset $interactionEntryPassage>><</btn>>
</div>
<<elseif _status === "showering">>
<<set $showerEncounterTarget = $interactingChar>>
<<set $showerEncounterReturnLoc = _returnLoc>>
<<if $interactingChar === "mother" || $interactingChar === "father" || $interactingChar === "brother">>
<<goto "showerEncounter">>
<<else>>
<<narrative _charLocName>>
<<print _char.firstName>> is in the shower right now.
<</narrative>>
<div class="location-actions">
<<btn "Leave" _returnLoc "secondary">><<unset $interactionEntryPassage>><</btn>>
</div>
<</if>>
<<elseif _status === "busy">>
<<narrative _charLocName>>
<<print _char.firstName>> seems to be occupied with something important right now. Maybe you should come back later.
<</narrative>>
<div class="location-actions">
<<btn "Leave" _returnLoc "secondary">><<unset $interactionEntryPassage>><</btn>>
</div>
<<elseif _status === "waking">>
<<narrative _charLocName>>
<<print _char.firstName>> is just waking up, stretching and yawning. They notice you and give a sleepy smile.
<</narrative>>
<<questPrompts $interactingChar>>
<<showActions $interactingChar _charLoc>>
<div class="location-actions">
<<btn "Leave" _returnLoc "secondary">><<unset $interactionEntryPassage>><</btn>>
</div>
<<else>>
<<narrative _charLocName>>
You approach <<print _char.firstName>>.
<</narrative>>
<<charInfoCard $interactingChar>>
<<questPrompts $interactingChar>>
<<showActions $interactingChar _charLoc>>
<div class="location-actions">
<<btn "Leave" _returnLoc "secondary">><<unset $interactionEntryPassage>><</btn>>
</div>
<</if>>
<</nobr>>/* ==================================================
CHARACTER DISPLAY WIDGETS
Widgets for rendering character cards in locations
================================================== */
/* Show Location Characters Widget
Usage: <<showLocationChars "kitchen">>
Automatically displays characters at the specified location
based on Time System schedule
*/
<<widget "showLocationChars">>
<<nobr>>
<<set _targetLocation = _args[0]>>
<<set _hasCharacter = false>>
/* Build list of locations to show characters from.
If this is a workplace mainLocation and player is NOT employed there,
aggregate characters from all subLocations (except excluded ones). */
<<set _showLocations = [_targetLocation]>>
<<set _excludeChars = []>>
<<set _wpGroup = null>>
<<for _wpKey, _wp range setup.workplaceGroups>>
<<if _wp.mainLocation === _targetLocation>>
<<set _wpGroup = _wp>>
<</if>>
<</for>>
<<if _wpGroup !== null>>
/* Check if player is employed at this workplace */
<<set _isEmployed = false>>
<<if $job && $job.id && setup.jobs[$job.id] && setup.jobs[$job.id].workplace === _wpGroup.mainLocation>>
<<set _isEmployed = true>>
<</if>>
<<if !_isEmployed>>
<<set _showLocations = _showLocations.concat(_wpGroup.subLocations)>>
<<set _excludeChars = _wpGroup.excludeFromAggregation || []>>
<</if>>
<</if>>
<div class="location-characters">
<<for _charId, _char range $characters>>
<<if _charId !== "player" && _showLocations.includes(_char.currentLocation) && _char.known === true && !_excludeChars.includes(_charId)>>
<<set _hasCharacter = true>>
<<capture _charId>>
<<set _charMerged = setup.getCharacter(_charId)>>
<<set _avatar = _charMerged.avatar>>
<<set _name = (_charMerged.firstName || "") + " " + (_charMerged.lastName || "")>>
<<set _status = _char.currentStatus>>
<<if _status === "busy">>
<div class="character-card-inner busy">
<div class="character-avatar">
<img @src="_avatar" @alt="_name">
</div>
<div class="character-name"><<print _name>></div>
<div class="character-status busy">Busy</div>
</div>
<<else>>
<a class="character-card-link" @data-char-id="_charId">
<div class="character-card-inner">
<div class="character-avatar">
<img @src="_avatar" @alt="_name">
</div>
<div class="character-name"><<print _name>></div>
</div>
</a>
<</if>>
<</capture>>
<</if>>
<</for>>
<<if !_hasCharacter>>
<p class="no-characters">No one is here right now.</p>
<</if>>
</div>
<<run
/* Click handler for character cards */
$(document).off('click.charcard').on('click.charcard', '.character-card-link', function(e) {
e.preventDefault();
const charId = $(this).attr('data-char-id');
State.variables.interactingChar = charId;
Engine.play('CharacterInteraction');
});
>>
<</nobr>>
<</widget>>
/* Character Link – inline link that opens CharacterInteraction for this character
Usage: <<charLink "dinerChef">> → shows "James" (firstName)
<<charLink "dinerChef" "the chef">> → shows "the chef"
Only shows link if character is known (or always for family). Optional third arg: force show even if not known (e.g. for intro scenes).
*/
<<widget "charLink">>
<<nobr>>
<<set _charId = _args[0]>>
<<set _char = setup.getCharacter(_charId)>>
<<if !_char>>
<<print _charId>>
<<else>>
<<set _label = _args[1] ? _args[1] : (_char.firstName || _char.name || _charId)>>
<<set _known = $characters[_charId].known === true || _charId === "player" || ["mother","father","brother"].includes(_charId)>>
<<set _forceShow = _args[2] === true>>
<<if _known || _forceShow>>
<<link _label>><<set $interactingChar = _charId>><<goto "CharacterInteraction">><</link>>
<<else>>
<<print _label>>
<</if>>
<</if>>
<</nobr>>
<</widget>>
/* Character Info Card Widget
Usage: <<charInfoCard "mother">>
Displays a compact info card with avatar, name, relationship, age, stats
*/
<<widget "charInfoCard">>
<<nobr>>
<<set _charId = _args[0]>>
<<set _char = setup.getCharacter(_charId)>>
<<if !_char>>
<p class="error">Character not found: <<print _charId>></p>
<<else>>
<<set _fullName = (_char.firstName || "") + " " + (_char.lastName || "")>>
<<set _age = ($timeSysYear || 2024) - (_char.birthYear || 2000)>>
<<set _stats = _char.stats || { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 }>>
<<set _thresholds = _char.levelUpThresholds || {}>>
<<set _opinion = _char.opinion || { awareness: 0, flags: [] }>>
<<set _awareness = _opinion.awareness || 0>>
/* Awareness tier */
<<if _awareness <= 10>>
<<set _tier = { name: "Innocent", color: "#4ade80" }>>
<<elseif _awareness <= 25>>
<<set _tier = { name: "Curious", color: "#60a5fa" }>>
<<elseif _awareness <= 45>>
<<set _tier = { name: "Suspicious", color: "#fbbf24" }>>
<<elseif _awareness <= 65>>
<<set _tier = { name: "Aware", color: "#f59e0b" }>>
<<elseif _awareness <= 85>>
<<set _tier = { name: "Knowing", color: "#f87171" }>>
<<else>>
<<set _tier = { name: "Complicit", color: "#ef4444" }>>
<</if>>
/* Stat bar calculations */
<<set _loveLevel = _stats.loveLevel || 1>>
<<set _friendLevel = _stats.friendshipLevel || 1>>
<<set _lustLevel = _stats.lustLevel || 1>>
<<set _trustLevel = _stats.trustLevel || 1>>
<<set _loveThr = ((_thresholds.love && _thresholds.love[_loveLevel] !== undefined) ? _thresholds.love[_loveLevel] : 100)>>
<<set _friendThr = ((_thresholds.friendship && _thresholds.friendship[_friendLevel] !== undefined) ? _thresholds.friendship[_friendLevel] : 100)>>
<<set _lustThr = ((_thresholds.lust && _thresholds.lust[_lustLevel] !== undefined) ? _thresholds.lust[_lustLevel] : 100)>>
<<set _trustThr = ((_thresholds.trust && _thresholds.trust[_trustLevel] !== undefined) ? _thresholds.trust[_trustLevel] : 100)>>
<<set _lovePct = Math.min(100, Math.round((_stats.love / _loveThr) * 100))>>
<<set _friendPct = Math.min(100, Math.round((_stats.friendship / _friendThr) * 100))>>
<<set _lustPct = Math.min(100, Math.round((_stats.lust / _lustThr) * 100))>>
<<set _trustPct = Math.min(100, Math.round((_stats.trust / _trustThr) * 100))>>
<div class="char-info-accordion">
<div class="char-info-header" onclick="this.parentElement.classList.toggle('expanded')">
<div class="char-info-avatar">
<img @src="_char.avatar" @alt="_fullName">
</div>
<div class="char-info-text">
<<print _fullName>> (<span class="char-relation"><<print _char.status || "Unknown">></span>), <<print _age>> years old
</div>
<span class="char-info-toggle">▼</span>
</div>
<div class="char-info-content">
<div class="char-info-stats-bars">
<div class="char-stat-bar-row">
<div class="char-stat-bar-label">
<span>Friendship</span>
<span class="char-stat-bar-meta"><span class="char-stat-level">Level <<print _friendLevel>></span> · <<print _stats.friendship>>/<<print _friendThr>></span>
</div>
<div class="char-stat-bar-track"><div class="char-stat-bar-fill" @style="'width:' + _friendPct + '%; background:#3b82f6;'"></div></div>
</div>
<div class="char-stat-bar-row">
<div class="char-stat-bar-label">
<span>Trust</span>
<span class="char-stat-bar-meta"><span class="char-stat-level">Level <<print _trustLevel>></span> · <<print _stats.trust>>/<<print _trustThr>></span>
</div>
<div class="char-stat-bar-track"><div class="char-stat-bar-fill" @style="'width:' + _trustPct + '%; background:#10b981;'"></div></div>
</div>
<div class="char-stat-bar-row">
<div class="char-stat-bar-label">
<span>Love</span>
<span class="char-stat-bar-meta"><span class="char-stat-level">Level <<print _loveLevel>></span> · <<print _stats.love>>/<<print _loveThr>></span>
</div>
<div class="char-stat-bar-track"><div class="char-stat-bar-fill" @style="'width:' + _lovePct + '%; background:#ec4899;'"></div></div>
</div>
<div class="char-stat-bar-row">
<div class="char-stat-bar-label">
<span>Lust</span>
<span class="char-stat-bar-meta"><span class="char-stat-level">Level <<print _lustLevel>></span> · <<print _stats.lust>>/<<print _lustThr>></span>
</div>
<div class="char-stat-bar-track"><div class="char-stat-bar-fill" @style="'width:' + _lustPct + '%; background:#ef4444;'"></div></div>
</div>
</div>
<div class="char-info-awareness">
Thinks you're : <span @style="'color:' + _tier.color + '; font-weight: 600;'"><<print _tier.name>></span>
</div>
</div>
</div>
<</if>>
<</nobr>>
<</widget>>/* ==========================================
CLOTHING STYLE CHECK WIDGETS
Reusable widgets for checking outfit appropriateness
========================================== */
/*
WIDGET: checkInventoryItem
Purpose: Check if player owns a specific item in inventory
Parameters (via _args):
[0] itemId - Inventory item ID to check (e.g., "yoga_mat")
[1] minQuantity - Minimum quantity required (default: 1)
Output: Sets State.temporary.inventoryCheckResult
{
allowed: boolean,
reason: string,
hasItem: boolean,
currentQuantity: number
}
Usage Examples:
<<checkInventoryItem "yoga_mat">>
<<checkInventoryItem "protein_shake" 3>>
*/
<<widget "checkInventoryItem">>
<<nobr>>
<<silently>>
/* Parse arguments */
<<set _itemId = _args[0] || "">>
<<set _minQuantity = _args[1] || 1>>
/* Initialize result */
<<set _result = {
allowed: false,
reason: "",
hasItem: false,
currentQuantity: 0
}>>
/* Check inventory */
<<if _itemId !== "">>
<<set _foundQuantity = 0>>
<<if $inventory && $inventory.length > 0>>
<<for _invItem range $inventory>>
<<if _invItem.id === _itemId>>
<<set _foundQuantity = _invItem.quantity || 1>>
<<break>>
<</if>>
<</for>>
<</if>>
<<set _result.currentQuantity = _foundQuantity>>
<<set _result.hasItem = (_foundQuantity > 0)>>
/* Check if requirement met */
<<if _foundQuantity >= _minQuantity>>
<<set _result.allowed = true>>
<<else>>
<<set _itemName = _itemId.replace(/_/g, " ")>>
<<set _result.reason = "You need " + _itemName + ".">>
<</if>>
<<else>>
<<set _result.allowed = true>>
<</if>>
/* Store result */
<<set State.temporary.inventoryCheckResult = _result>>
<</silently>>
<</nobr>>
<</widget>>
/*
WIDGET: checkOutfitStyle
Purpose: Check if current outfit matches required style/looks for activities
Parameters (via _args):
[0] requiredStyles - String or Array of acceptable style tags (e.g., "sporty" or ["sporty", "casual"])
[1] minStyleItems - Minimum number of items with required style (default: 1)
[2] minLooks - Minimum total looks value required (default: 0)
Output: Sets State.temporary.outfitCheckResult
{
allowed: boolean,
reason: string,
styleCount: number,
totalLooks: number,
matchingStyles: array
}
Usage Examples:
1. Yoga - requires sporty outfit (1+ items)
<<checkOutfitStyle "sporty" 1 0>>
2. Gym - requires sporty outfit (2+ items) + 15 looks
<<checkOutfitStyle "sporty" 2 15>>
3. Business meeting - requires business/professional/formal (2+ items) + 20 looks
<<checkOutfitStyle ["business", "professional", "formal"] 2 20>>
4. Casual hangout - just needs 10+ looks, any style
<<checkOutfitStyle [] 0 10>>
*/
<<widget "checkOutfitStyle">>
<<nobr>>
<<silently>>
/* Parse arguments */
<<set _requiredStyles = _args[0] || []>>
<<set _minStyleItems = _args[1] || 1>>
<<set _minLooks = _args[2] || 0>>
/* Normalize requiredStyles to array */
<<if typeof _requiredStyles === "string">>
<<if _requiredStyles === "">>
<<set _requiredStyles = []>>
<<else>>
<<set _requiredStyles = [_requiredStyles]>>
<</if>>
<</if>>
/* Initialize result object */
<<set _result = {
allowed: true,
reason: "",
styleCount: 0,
totalLooks: 0,
matchingStyles: []
}>>
/* ========== STYLE CHECK ========== */
<<if _requiredStyles.length > 0>>
<<set _styleCount = 0>>
<<set _matchingStyles = []>>
/* Check ALL equipped slots, dedupe same itemId (bodysuit/swimsuit occupies bra+panty) */
<<if $wardrobe && $wardrobe.equipped>>
<<set _seenStyleItemIds = []>>
<<for _slot, _itemId range $wardrobe.equipped>>
<<if _itemId && _itemId !== "" && !_seenStyleItemIds.includes(_itemId)>>
<<run _seenStyleItemIds.push(_itemId)>>
<<set _slotItem = setup.getClothingById(_itemId)>>
<<if _slotItem && _slotItem.tags>>
<<set _matchedThisItem = false>>
<<for _reqStyle range _requiredStyles>>
<<if _slotItem.tags.includes(_reqStyle)>>
<<set _matchedThisItem = true>>
<<if !_matchingStyles.includes(_reqStyle)>>
<<run _matchingStyles.push(_reqStyle)>>
<</if>>
<</if>>
<</for>>
<<if _matchedThisItem>>
<<set _styleCount += 1>>
<</if>>
<</if>>
<</if>>
<</for>>
<</if>>
<<set _result.styleCount = _styleCount>>
<<set _result.matchingStyles = _matchingStyles>>
/* Check if minimum style items requirement met */
<<if _styleCount < _minStyleItems>>
<<set _result.allowed = false>>
<<set _styleNames = _requiredStyles.join(" or ")>>
<<set _result.reason = "You need " + _styleNames + " outfit.">>
<</if>>
<</if>>
/* ========== LOOKS CHECK ========== */
<<if _minLooks > 0>>
<<set _totalLooks = 0>>
<<set _seenItemIds = []>>
<<if $wardrobe && $wardrobe.equipped>>
<<for _slot, _itemId range $wardrobe.equipped>>
<<if _itemId && _itemId !== "" && !_seenItemIds.includes(_itemId)>>
<<run _seenItemIds.push(_itemId)>>
<<set _item = setup.getClothingById(_itemId)>>
<<if _item>>
<<if def setup.getEffectiveLooks>>
<<set _totalLooks += setup.getEffectiveLooks(_item, _itemId)>>
<<elseif _item.looks>>
<<set _totalLooks += _item.looks>>
<</if>>
<</if>>
<</if>>
<</for>>
<</if>>
<<set _result.totalLooks = _totalLooks>>
/* Check if minimum looks requirement met */
<<if _totalLooks < _minLooks>>
<<set _result.allowed = false>>
<<if _result.reason !== "">>
<<set _result.reason += " Also, your outfit needs at least +" + _minLooks + " Looks (current: +" + _totalLooks + ").">>
<<else>>
<<set _result.reason = "Your outfit needs at least +" + _minLooks + " Looks (current: +" + _totalLooks + ").">>
<</if>>
<</if>>
<</if>>
/* Store result in State.temporary */
<<set State.temporary.outfitCheckResult = _result>>
<</silently>>
<</nobr>>
<</widget>>
/*
WIDGET: showOutfitCheckError
Purpose: Display outfit check error message with "Change Clothes" and "Back" buttons
Parameters (via _args):
[0] returnPassage - Where to go when clicking "Back" (default: previous passage)
Usage:
<<showOutfitCheckError "fhLivingroom">>
*/
<<widget "showOutfitCheckError">>
<<nobr>>
<<set _returnPassage = _args[0] || passage()>>
<<if State.temporary.outfitCheckResult && !State.temporary.outfitCheckResult.allowed>>
<<narrative "Wrong Outfit">>
<<print State.temporary.outfitCheckResult.reason>>
<</narrative>>
<div class="location-actions">
<<btn "Change Clothes" "Wardrobe">><</btn>>
<<btn "Back" _returnPassage>><</btn>>
</div>
<</if>>
<</nobr>>
<</widget>>
/*
WIDGET: getOutfitStyleSummary
Purpose: Get a human-readable summary of current outfit style
Output: Sets State.temporary.outfitStyleSummary
{
dominantStyle: "sporty",
dominantStyleCount: 3,
allStyles: ["sporty", "casual"],
totalLooks: 15,
description: "Athletic Sportswear"
}
*/
<<widget "getOutfitStyleSummary">>
<<nobr>>
<<silently>>
<<set _styleCounts = {}>>
<<set _totalLooks = 0>>
<<set _seenItemIds = []>>
/* Count style occurrences in equipped items */
<<if $wardrobe && $wardrobe.equipped>>
<<for _slot, _itemId range $wardrobe.equipped>>
<<if _itemId && _itemId !== "" && !_seenItemIds.includes(_itemId)>>
<<run _seenItemIds.push(_itemId)>>
<<set _item = setup.getClothingById(_itemId)>>
<<if _item>>
/* Count looks (effective if available) */
<<if def setup.getEffectiveLooks>>
<<set _totalLooks += setup.getEffectiveLooks(_item, _itemId)>>
<<elseif _item.looks>>
<<set _totalLooks += _item.looks>>
<</if>>
/* Count styles */
<<if _item.tags && _item.tags.length > 0>>
<<for _tag range _item.tags>>
/* Only count style tags */
<<set _styleTagsList = ["casual", "sporty", "business", "professional", "formal", "elegant", "cute", "sexy", "revealing", "daring", "erotic", "lewd", "slutty", "bimbo"]>>
<<if _styleTagsList.includes(_tag)>>
<<if !_styleCounts[_tag]>>
<<set _styleCounts[_tag] = 0>>
<</if>>
<<set _styleCounts[_tag] += 1>>
<</if>>
<</for>>
<</if>>
<</if>>
<</if>>
<</for>>
<</if>>
/* Find dominant style */
<<set _dominantStyle = "casual">>
<<set _dominantCount = 0>>
<<set _allStyles = []>>
<<for _style, _count range _styleCounts>>
<<run _allStyles.push(_style)>>
<<if _count > _dominantCount>>
<<set _dominantCount = _count>>
<<set _dominantStyle = _style>>
<</if>>
<</for>>
/* Generate description */
<<set _descriptions = {
casual: "Casual Everyday Wear",
sporty: "Athletic Sportswear",
business: "Business Attire",
professional: "Professional Outfit",
formal: "Formal Wear",
elegant: "Elegant Ensemble",
cute: "Cute Outfit",
sexy: "Sexy Outfit",
revealing: "Revealing Attire",
daring: "Daring Outfit",
erotic: "Erotic Wear",
lewd: "Lewd Outfit",
slutty: "Slutty Attire",
bimbo: "Bimbo Look"
}>>
<<set _description = _descriptions[_dominantStyle] || "Mixed Style Outfit">>
/* Store result */
<<set State.temporary.outfitStyleSummary = {
dominantStyle: _dominantStyle,
dominantStyleCount: _dominantCount,
allStyles: _allStyles,
totalLooks: _totalLooks,
description: _description
}>>
<</silently>>
<</nobr>>
<</widget>>
/* ==========================================
COMPUTER WIDGET
Handles brother's computer interaction
- Quest prompts when quest is active
- Availability check (brother presence)
- Brother PC menu (`brotherComputerMenu`)
========================================== */
<<widget "useComputerWidget">><<nobr>>
<<silently>>
/* Check if quest is completed: flag or completed list */
<<set _computerQuestDone = $used_computer === true ||
($questState && $questState.completed && $questState.completed.includes("use_computer"))>>
/* Brother in room = PC locked (schedule uses currentLocation) */
<<set _brotherOnPC = $characters.brother &&
$characters.brother.currentLocation === "fhBrotherRoom">>
<</silently>>
<<if _computerQuestDone && _brotherOnPC>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Your brother is on the PC right now."><span class="icon icon-lock icon-12"></span> Use Computer</span>
<<elseif _computerQuestDone>>
<<btn "Use Computer" "brotherComputerMenu">><</btn>>
<</if>>
<</nobr>><</widget>>
<<widget "couchActions">>
<<nobr>>
<<set _presentFamily = []>>
<<if $characters.mother.currentLocation === "fhLivingroom">>
<<run _presentFamily.push("Mom")>>
<</if>>
<<if $characters.father.currentLocation === "fhLivingroom">>
<<run _presentFamily.push("Dad")>>
<</if>>
<<if $characters.brother.currentLocation === "fhLivingroom">>
<<run _presentFamily.push("Brother")>>
<</if>>
<div class="location-actions">
<<set $activityOrigin = "fhLivingroom">>
<<readBtn>>
<<btnPicker "Nap" "runNap" "napDuration">>
<<if _presentFamily.length === 0>>
<<set $watchingWith = "alone">>
<<btnPicker "Watch TV" "watchTV" "tvDuration" "default" "tvWatch">>
<<elseif _presentFamily.length === 1>>
<<set $watchingWith = _presentFamily[0].toLowerCase()>>
<<btnPicker ` "Watch TV with " + _presentFamily[0] ` "watchTV" "tvDuration" "default" "tvWatch">>
<<elseif _presentFamily.length === 2>>
<<if _presentFamily.includes("Mom") && _presentFamily.includes("Dad")>>
<<set $watchingWith = "momdad">>
<<btnPicker "Watch TV with Mom & Dad" "watchTV" "tvDuration" "default" "tvWatch">>
<<elseif _presentFamily.includes("Mom") && _presentFamily.includes("Brother")>>
<<set $watchingWith = "mombrother">>
<<btnPicker "Watch TV with Mom & Brother" "watchTV" "tvDuration" "default" "tvWatch">>
<<elseif _presentFamily.includes("Dad") && _presentFamily.includes("Brother")>>
<<set $watchingWith = "dadbrother">>
<<btnPicker "Watch TV with Dad & Brother" "watchTV" "tvDuration" "default" "tvWatch">>
<<else>>
<<set $watchingWith = "family">>
<<btnPicker "Watch TV with Family" "watchTV" "tvDuration" "default" "tvWatch">>
<</if>>
<<else>>
<<set $watchingWith = "family">>
<<btnPicker "Watch TV with Family" "watchTV" "tvDuration" "default" "tvWatch">>
<</if>>
</div>
<</nobr>>
<</widget>><<script>>
/* =====================================================================
DURATION PRESETS - Time options for btnPicker macro
=====================================================================
Usage in passages:
<<btnPicker "Watch TV" "watchTV" "tvDuration">>
Then in the target passage:
<<advanceTime $selectedDuration>>
===================================================================== */
/* Merge so hub / other scripts can register presets first; avoids losing keys by load order. */
setup.durationPresets = Object.assign({}, setup.durationPresets || {}, {
/* ==========================================
ENTERTAINMENT
========================================== */
"tvDuration": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
],
"gamingDuration": [
{ label: "30 min", value: 30 },
{ label: "1 hour", value: 60 },
{ label: "2 hours", value: 120 },
{ label: "3 hours", value: 180 }
],
"readingDuration": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "1 hour", value: 60 }
],
/* ==========================================
SLEEP & REST
========================================== */
"sleepDuration": [
{ label: "6 hours", value: 360 },
{ label: "7 hours", value: 420 },
{ label: "8 hours", value: 480 },
{ label: "9 hours", value: 540 }
],
"napDuration": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
],
"snoozeDuration": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "60 min", value: 60 }
],
/* ==========================================
SHORT ACTIVITIES
========================================== */
"shortActivity": [
{ label: "5 min", value: 5 },
{ label: "10 min", value: 10 },
{ label: "15 min", value: 15 },
{ label: "20 min", value: 20 }
],
"mediumActivity": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
],
/* ==========================================
EXERCISE & FITNESS
========================================== */
"workoutDuration": [
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 },
{ label: "1.5 hours", value: 90 }
],
"yogaDuration": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
],
/* ==========================================
HYGIENE
========================================== */
"showerDuration": [
{ label: "5 min", value: 5 },
{ label: "10 min", value: 10 },
{ label: "15 min", value: 15 },
{ label: "20 min", value: 20 }
],
"bathDuration": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
],
/* ==========================================
JOB SHIFTS (values in minutes: 120, 240, 360, 480)
========================================== */
"jobShiftDuration": [
{ label: "2 hours", value: 120 },
{ label: "4 hours", value: 240 },
{ label: "6 hours", value: 360 },
{ label: "8 hours", value: 480 }
],
/* District hub btnPicker uses preset[0] as default when no pickerMemory; keep shortest first */
"hubAmbientWalk": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
],
"hubAmbientPhone": [
{ label: "10 min", value: 10 },
{ label: "15 min", value: 15 },
{ label: "20 min", value: 20 },
{ label: "30 min", value: 30 }
],
"hubAmbientWatch": [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
]
});
<</script>><<nobr>>
<<set setup.emmaTopics = setup.emmaTopics || {}>>
<<set setup.emmaTopics.common = setup.emmaTopics.common || {}>>
<<set setup.emmaTopics.common.level1 = {
"dinerRubys_common_morning": {
"tired_morning": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-1.webp">>
<<image _img "100%">>
<<narrative>>Emma passes by with a stack of menus, tucking a strand of hair behind her ear without slowing down.<</narrative>>
<<dialog "dinerWaitress1">>Morning. Need a table or just browsing?<</dialog>>
<<dialog "player">>Just stopped in. Busy already?<</dialog>>
<<dialog "dinerWaitress1">>It's always busy. Or about to be.<</dialog>>
<<dialog "player">>Sounds exhausting this early.<</dialog>>
<<dialog "dinerWaitress1">>You get used to it. Sort of.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-2.webp">>
<<image _img "100%">>
<<narrative>>Emma wipes down a booth with one hand, balancing a tray of empty mugs on the other. She barely glances up.<</narrative>>
<<dialog "dinerWaitress1">>Coffee's fresh if you want some.<</dialog>>
<<dialog "player">>You look like you need it more.<</dialog>>
<<dialog "dinerWaitress1">>Had two cups. Didn't help.<</dialog>>
<<dialog "player">>Maybe a third one will do it.<</dialog>>
<<dialog "dinerWaitress1">>That's the theory. Hasn't worked yet.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-3.webp">>
<<image _img "100%">>
<<narrative>>Emma straightens a chair with her hip as she walks past, eyes scanning the room like she's running a checklist.<</narrative>>
<<dialog "dinerWaitress1">>Sorry, one sec. Table six needs clearing.<</dialog>>
<<dialog "player">>No rush. I'm just hanging around.<</dialog>>
<<dialog "dinerWaitress1">>Must be nice.<</dialog>>
<<dialog "player">>Didn't mean it like that.<</dialog>>
<<dialog "dinerWaitress1">>I know. Just jealous.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"the_rush": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-4.webp">>
<<image _img "100%">>
<<narrative>>Emma slides a plate onto a table without breaking stride, already turning toward the next booth.<</narrative>>
<<dialog "dinerWaitress1">>Breakfast crowd's the worst. Everyone wants eggs.<</dialog>>
<<dialog "player">>Eggs are pretty good here though.<</dialog>>
<<dialog "dinerWaitress1">>Sure. Making fifty of them isn't.<</dialog>>
<<dialog "player">>Fair point. James back there alone?<</dialog>>
<<dialog "dinerWaitress1">>James can handle it. I'm the one running them out.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-5.webp">>
<<image _img "100%">>
<<narrative>>Emma refills a napkin dispenser at the counter, moving fast like she's on a timer.<</narrative>>
<<dialog "dinerWaitress1">>Morning rush hit early today.<</dialog>>
<<dialog "player">>Looks like it's calming down now.<</dialog>>
<<dialog "dinerWaitress1">>Give it ten minutes. Second wave always comes.<</dialog>>
<<dialog "player">>There's a second wave?<</dialog>>
<<dialog "dinerWaitress1">>There's always a second wave.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-6.webp">>
<<image _img "100%">>
<<narrative>>Emma drops off a check at a table, clicking her pen shut as she turns back toward the counter.<</narrative>>
<<dialog "dinerWaitress1">>Mornings move fast. Blink and it's noon.<</dialog>>
<<dialog "player">>That a good thing or bad thing?<</dialog>>
<<dialog "dinerWaitress1">>Depends on the tips.<</dialog>>
<<dialog "player">>How are they today?<</dialog>>
<<dialog "dinerWaitress1">>Don't ask.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_common_afternoon": {
"sofia_complaint": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Emma sets down a tray of dirty dishes on the counter, rolling her eyes at something across the room.<</narrative>>
<<dialog "dinerWaitress1">>Sofia's on her phone again. Shocking.<</dialog>>
<<dialog "player">>She does seem pretty relaxed over there.<</dialog>>
<<dialog "dinerWaitress1">>Relaxed is one word for it.<</dialog>>
<<dialog "player">>You ever say anything to her?<</dialog>>
<<dialog "dinerWaitress1">>Not worth the drama.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Emma picks up a plate someone left on the wrong table, sighing through her nose.<</narrative>>
<<dialog "dinerWaitress1">>I've got her tables and mine right now.<</dialog>>
<<dialog "player">>Where's Sofia?<</dialog>>
<<dialog "dinerWaitress1">>Good question. Probably texting her boyfriend.<</dialog>>
<<dialog "player">>That doesn't bother the manager?<</dialog>>
<<dialog "dinerWaitress1">>Vince picks his battles. I just pick up the slack.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Emma wipes her hands on her apron, glancing toward the back of the diner with a flat expression.<</narrative>>
<<dialog "dinerWaitress1">>Some people work. Some people show up.<</dialog>>
<<dialog "player">>Talking about anyone in particular?<</dialog>>
<<dialog "dinerWaitress1">>You've been here five minutes. You tell me.<</dialog>>
<<dialog "player">>The other waitress doesn't seem too busy.<</dialog>>
<<dialog "dinerWaitress1">>Understatement of the year.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"closing_time": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Emma leans on the counter for half a second, checking the clock above the register before snapping back to work.<</narrative>>
<<dialog "dinerWaitress1">>Still got hours to go. Feels like days.<</dialog>>
<<dialog "player">>Long shift?<</dialog>>
<<dialog "dinerWaitress1">>They're all long shifts.<</dialog>>
<<dialog "player">>When do you get off?<</dialog>>
<<dialog "dinerWaitress1">>Not soon enough.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Emma stacks menus at the host stand, her movements mechanical, like she could do this in her sleep.<</narrative>>
<<dialog "dinerWaitress1">>Afternoon's the worst. Too slow to stay busy, too long to feel short.<</dialog>>
<<dialog "player">>The in-between hours.<</dialog>>
<<dialog "dinerWaitress1">>Exactly. Clock just stops moving.<</dialog>>
<<dialog "player">>Maybe that's when you take it easy.<</dialog>>
<<dialog "dinerWaitress1">>Doesn't work like that here.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Emma refills the sugar caddies at the counter, one by one, not looking up.<</narrative>>
<<dialog "dinerWaitress1">>Closing can't come fast enough today.<</dialog>>
<<dialog "player">>Rough afternoon?<</dialog>>
<<dialog "dinerWaitress1">>Table two sent their food back. Twice.<</dialog>>
<<dialog "player">>Ouch. What was wrong with it?<</dialog>>
<<dialog "dinerWaitress1">>Nothing. Some people just like complaining.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.emmaTopics = setup.emmaTopics || {}>>
<<set setup.emmaTopics.dishwasherDone = setup.emmaTopics.dishwasherDone || {}>>
<<set setup.emmaTopics.dishwasherDone.level1 = {
"dinerRubys_dishwasherDone_morning": {
"winding_down": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-13.webp">>
<<image _img "100%">>
<<narrative>>Emma unties her apron slowly, folding it over her arm. She looks lighter already.<</narrative>>
<<dialog "dinerWaitress1">>Done. Finally done.<</dialog>>
<<dialog "player">>You look relieved.<</dialog>>
<<dialog "dinerWaitress1">>That obvious, huh?<</dialog>>
<<dialog "player">>Little bit. Big plans for the rest of the day?<</dialog>>
<<dialog "dinerWaitress1">>Couch. That's the plan. The whole plan.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-14.webp">>
<<image _img "100%">>
<<narrative>>Emma leans on the counter, watching the next shift start to trickle in. She rolls her shoulders back.<</narrative>>
<<dialog "dinerWaitress1">>Handed off my tables. Not my problem anymore.<</dialog>>
<<dialog "player">>Must feel good to let go.<</dialog>>
<<dialog "dinerWaitress1">>For about five minutes. Then I start thinking about tomorrow.<</dialog>>
<<dialog "player">>Try not to think about it.<</dialog>>
<<dialog "dinerWaitress1">>Easier said than done.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-15.webp">>
<<image _img "100%">>
<<narrative>>Emma counts some bills near the register, tucking them into her pocket. She's already half out the door mentally.<</narrative>>
<<dialog "dinerWaitress1">>Tips were okay today. Not great. Okay.<</dialog>>
<<dialog "player">>Okay is better than bad.<</dialog>>
<<dialog "dinerWaitress1">>That's the spirit I guess. Low bar though.<</dialog>>
<<dialog "player">>Glass half full?<</dialog>>
<<dialog "dinerWaitress1">>Glass is whatever it is. I'm going home.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"off_the_clock": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-16.webp">>
<<image _img "100%">>
<<narrative>>Emma sits at the counter, actually sitting for once, nursing a glass of water like she just ran a marathon.<</narrative>>
<<dialog "dinerWaitress1">>Weird being on this side of the counter.<</dialog>>
<<dialog "player">>You never sit here?<</dialog>>
<<dialog "dinerWaitress1">>Only after a shift. Feels wrong otherwise.<</dialog>>
<<dialog "player">>You earned it today though.<</dialog>>
<<dialog "dinerWaitress1">>I earn it every day. Today I'm actually taking it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-17.webp">>
<<image _img "100%">>
<<narrative>>Emma checks her phone for the first time all shift, scrolling through with a blank expression.<</narrative>>
<<dialog "dinerWaitress1">>Twelve missed messages. All junk.<</dialog>>
<<dialog "player">>Nobody important?<</dialog>>
<<dialog "dinerWaitress1">>Nobody who needs me alive before noon.<</dialog>>
<<dialog "player">>At least that means no emergencies.<</dialog>>
<<dialog "dinerWaitress1">>True. I'll take boring over crisis any day.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-18.webp">>
<<image _img "100%">>
<<narrative>>Emma pulls her hair out of its ponytail, shaking it loose. She looks five years younger without the work face on.<</narrative>>
<<dialog "dinerWaitress1">>That ponytail was giving me a headache all morning.<</dialog>>
<<dialog "player">>First thing you do when you're done?<</dialog>>
<<dialog "dinerWaitress1">>Hair down. Shoes off. In that order.<</dialog>>
<<dialog "player">>Sounds like a ritual.<</dialog>>
<<dialog "dinerWaitress1">>It is. Very sacred. Don't interrupt it.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_dishwasherDone_afternoon": {
"tired_relief": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-13.webp">>
<<image _img "100%">>
<<narrative>>Emma drops her apron on the counter and exhales like she's been holding her breath for eight hours.<</narrative>>
<<dialog "dinerWaitress1">>Survived another one.<</dialog>>
<<dialog "player">>You say that like it was a battle.<</dialog>>
<<dialog "dinerWaitress1">>Some days it is. Today was a skirmish at least.<</dialog>>
<<dialog "player">>Could've been worse?<</dialog>>
<<dialog "dinerWaitress1">>It can always be worse. That's the motto.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-14.webp">>
<<image _img "100%">>
<<narrative>>Emma stretches her back, hands on her hips, cracking something that probably shouldn't crack.<</narrative>>
<<dialog "dinerWaitress1">>My back just made a noise. That's not great.<</dialog>>
<<dialog "player">>Maybe you should stretch more during shifts.<</dialog>>
<<dialog "dinerWaitress1">>When? Between table four and table seven?<</dialog>>
<<dialog "player">>Good point. After work then.<</dialog>>
<<dialog "dinerWaitress1">>After work I'm horizontal. That counts.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-15.webp">>
<<image _img "100%">>
<<narrative>>Emma leans on the host stand, watching Sofia take over her section. She almost smiles.<</narrative>>
<<dialog "dinerWaitress1">>Her problem now. All of it.<</dialog>>
<<dialog "player">>Passing the torch?<</dialog>>
<<dialog "dinerWaitress1">>Passing the headache. Table two's still complaining.<</dialog>>
<<dialog "player">>Did you warn her?<</dialog>>
<<dialog "dinerWaitress1">>Nope. She'll figure it out.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"heading_home": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-16.webp">>
<<image _img "100%">>
<<narrative>>Emma grabs her jacket from under the counter, throwing it over one arm. She looks like she's already gone mentally.<</narrative>>
<<dialog "dinerWaitress1">>I'm out. Clock's done. I'm done.<</dialog>>
<<dialog "player">>Heading straight home?<</dialog>>
<<dialog "dinerWaitress1">>Grocery store first. Then home. Then nothing.<</dialog>>
<<dialog "player">>Living the dream.<</dialog>>
<<dialog "dinerWaitress1">>Somebody's got to.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-17.webp">>
<<image _img "100%">>
<<narrative>>Emma pats her pockets phone, keys, tips. The end-of-shift checklist.<</narrative>>
<<dialog "dinerWaitress1">>Phone. Keys. Money. That's everything.<</dialog>>
<<dialog "player">>You do that every time?<</dialog>>
<<dialog "dinerWaitress1">>Left my keys here once. Never again.<</dialog>>
<<dialog "player">>How bad was that?<</dialog>>
<<dialog "dinerWaitress1">>Sat outside my apartment for two hours bad.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-18.webp">>
<<image _img "100%">>
<<narrative>>Emma stands by the door, looking back at the diner one last time. Not fondly just checking.<</narrative>>
<<dialog "dinerWaitress1">>Place doesn't fall apart without me. Probably.<</dialog>>
<<dialog "player">>You don't sound too sure about that.<</dialog>>
<<dialog "dinerWaitress1">>I'm not. But that's tomorrow's problem.<</dialog>>
<<dialog "player">>Smart way to think about it.<</dialog>>
<<dialog "dinerWaitress1">>Only way to survive this job.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysKitchen_dishwasherDone_afternoon": {
"kitchen_escape": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-7.webp">>
<<image _img "100%">>
<<narrative>>Emma grabs a glass of water from the kitchen tap, drinking it in one go. She looks wrung out.<</narrative>>
<<dialog "dinerWaitress1">>Just grabbing water. Then I'm gone.<</dialog>>
<<dialog "player">>You look like you need more than water.<</dialog>>
<<dialog "dinerWaitress1">>Sleep. I need sleep. Water's the preview.<</dialog>>
<<dialog "player">>How many hours did you do today?<</dialog>>
<<dialog "dinerWaitress1">>Enough that I stopped counting around lunch.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-8.webp">>
<<image _img "100%">>
<<narrative>>Emma leans against the kitchen doorframe, half in and half out, like she can't decide if she's still working or not.<</narrative>>
<<dialog "dinerWaitress1">>James is still going. That man's not human.<</dialog>>
<<dialog "player">>He does seem pretty locked in back there.<</dialog>>
<<dialog "dinerWaitress1">>He'll be here another three hours. I couldn't do it.<</dialog>>
<<dialog "player">>Different kind of stamina I guess.<</dialog>>
<<dialog "dinerWaitress1">>Different kind of crazy is what it is.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-9.webp">>
<<image _img "100%">>
<<narrative>>Emma drops off her notepad and pen on the kitchen shelf, like shedding armor after a fight.<</narrative>>
<<dialog "dinerWaitress1">>No more tickets. No more orders. Done.<</dialog>>
<<dialog "player">>Symbolic moment?<</dialog>>
<<dialog "dinerWaitress1">>Very. Like putting down a weapon.<</dialog>>
<<dialog "player">>Dramatic for a notepad.<</dialog>>
<<dialog "dinerWaitress1">>You haven't carried one for eight hours straight.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"post_shift_honesty": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-10.webp">>
<<image _img "100%">>
<<narrative>>Emma stands by the back exit, pulling her hair tie out. The kitchen noise fades behind her.<</narrative>>
<<dialog "dinerWaitress1">>This place smells like grease. I smell like grease.<</dialog>>
<<dialog "player">>Could be worse. Could smell like the dishwasher station.<</dialog>>
<<dialog "dinerWaitress1">>Fair. Mike's got it rough over there.<</dialog>>
<<dialog "player">>He seems to handle it okay.<</dialog>>
<<dialog "dinerWaitress1">>He's young. Everything's okay when you're young.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-11.webp">>
<<image _img "100%">>
<<narrative>>Emma rubs her eyes with the heels of her hands, blinking hard after.<</narrative>>
<<dialog "dinerWaitress1">>My eyes are dry. AC in here is brutal.<</dialog>>
<<dialog "player">>Kitchen doesn't have great ventilation either.<</dialog>>
<<dialog "dinerWaitress1">>Nothing in this building works right. Part of the charm.<</dialog>>
<<dialog "player">>That's one way to look at it.<</dialog>>
<<dialog "dinerWaitress1">>It's the only way. Otherwise you'd cry.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-12.webp">>
<<image _img "100%">>
<<narrative>>Emma leans on the stainless steel counter, watching the kitchen wind down. She looks almost peaceful.<</narrative>>
<<dialog "dinerWaitress1">>Kitchen's quieter when the rush is over. Almost nice.<</dialog>>
<<dialog "player">>You like it better back here when it's calm?<</dialog>>
<<dialog "dinerWaitress1">>Better than the floor. Nobody's asking me for refills.<</dialog>>
<<dialog "player">>Small victories.<</dialog>>
<<dialog "dinerWaitress1">>Only kind I get.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherDone_afternoon": {
"decompressing": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-7.webp">>
<<image _img "100%">>
<<narrative>>Emma sits on the break stool, shoes off, feet flat on the cold floor. She has her eyes closed and looks like she might not move for a while.<</narrative>>
<<dialog "dinerWaitress1">>Cold floor. Best part of this room.<</dialog>>
<<dialog "player">>Feels good on the feet?<</dialog>>
<<dialog "dinerWaitress1">>Feels good on everything. I might just stay here.<</dialog>>
<<dialog "player">>Nobody's gonna kick you out?<</dialog>>
<<dialog "dinerWaitress1">>I'm off the clock. They can try.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-8.webp">>
<<image _img "100%">>
<<narrative>>Emma pours the last bit of coffee from the machine, staring into the mug like it holds answers.<</narrative>>
<<dialog "dinerWaitress1">>This is my reward. Terrible coffee in a quiet room.<</dialog>>
<<dialog "player">>Sounds peaceful at least.<</dialog>>
<<dialog "dinerWaitress1">>It is. First peace I've had all day.<</dialog>>
<<dialog "player">>You deserve more than bad coffee though.<</dialog>>
<<dialog "dinerWaitress1">>Yeah, well. Take what you can get.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-9.webp">>
<<image _img "100%">>
<<narrative>>Emma leans against the wall, head back, staring at the ceiling. She looks emptied out but calm.<</narrative>>
<<dialog "dinerWaitress1">>There's a water stain up there shaped like Florida.<</dialog>>
<<dialog "player">>You've been staring at the ceiling a while, huh?<</dialog>>
<<dialog "dinerWaitress1">>Long enough to name the stains. That one's Florida. That one's a dog.<</dialog>>
<<dialog "player">>That's either relaxing or concerning.<</dialog>>
<<dialog "dinerWaitress1">>Bit of both. Mostly relaxing right now.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"real_talk": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-10.webp">>
<<image _img "100%">>
<<narrative>>Emma sits with her jacket on, ready to leave but not leaving yet. Like she needs a minute before facing the outside.<</narrative>>
<<dialog "dinerWaitress1">>Sometimes I sit here a few minutes after clocking out. Just to breathe.<</dialog>>
<<dialog "player">>I get that. Transition time.<</dialog>>
<<dialog "dinerWaitress1">>Exactly. Work brain to real brain. Takes a second.<</dialog>>
<<dialog "player">>Which one are you in right now?<</dialog>>
<<dialog "dinerWaitress1">>Somewhere in between. Ask me in five minutes.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-11.webp">>
<<image _img "100%">>
<<narrative>>Emma scrolls through her phone, propped up on stacked boxes. The storage room is quiet except for the hum of the building.<</narrative>>
<<dialog "dinerWaitress1">>My friend keeps texting about some bar tonight.<</dialog>>
<<dialog "player">>You going?<</dialog>>
<<dialog "dinerWaitress1">>I'd have to shower, change, be a person. That's a lot of steps.<</dialog>>
<<dialog "player">>Sounds like a no.<</dialog>>
<<dialog "dinerWaitress1">>Sounds like a "probably not but I'll feel guilty." Yeah.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-12.webp">>
<<image _img "100%">>
<<narrative>>Emma zips up her jacket, standing but not walking toward the door. She looks at the storage room like she's saying goodbye to it.<</narrative>>
<<dialog "dinerWaitress1">>Weird how this room feels more like mine than anywhere else here.<</dialog>>
<<dialog "player">>Your little corner of the diner?<</dialog>>
<<dialog "dinerWaitress1">>Something like that. Nobody bothers me here.<</dialog>>
<<dialog "player">>Except me, right now.<</dialog>>
<<dialog "dinerWaitress1">>You're alright. At least you don't need a refill.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.emmaTopics = setup.emmaTopics || {}>>
<<set setup.emmaTopics.dishwasherOnBreak = setup.emmaTopics.dishwasherOnBreak || {}>>
<<set setup.emmaTopics.dishwasherOnBreak.level1 = {
"dinerRubys_dishwasherOnBreak_morning": {
"feet_hurting": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-7.webp">>
<<image _img "100%">>
<<narrative>>Emma stands near the register, shifting her weight from one foot to the other. She flexes one ankle, wincing slightly.<</narrative>>
<<dialog "dinerWaitress1">>My feet are killing me already.<</dialog>>
<<dialog "player">>It's still morning though.<</dialog>>
<<dialog "dinerWaitress1">>Exactly. That's the problem.<</dialog>>
<<dialog "player">>Bad shoes or bad floors?<</dialog>>
<<dialog "dinerWaitress1">>Both. Definitely both.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-8.webp">>
<<image _img "100%">>
<<narrative>>Emma leans on the counter, pressing one foot flat against the floor like she's stretching it out.<</narrative>>
<<dialog "dinerWaitress1">>Stood for four hours straight. Can feel every one.<</dialog>>
<<dialog "player">>You should sit down for a bit.<</dialog>>
<<dialog "dinerWaitress1">>Break's only fifteen minutes. Rather just lean.<</dialog>>
<<dialog "player">>Fifteen minutes isn't much.<</dialog>>
<<dialog "dinerWaitress1">>It's enough to remember I'm tired.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-9.webp">>
<<image _img "100%">>
<<narrative>>Emma kicks one shoe off under the counter, then puts it right back on with a sigh.<</narrative>>
<<dialog "dinerWaitress1">>One day I'll buy decent shoes.<</dialog>>
<<dialog "player">>What's stopping you?<</dialog>>
<<dialog "dinerWaitress1">>Good ones cost more than a shift pays.<</dialog>>
<<dialog "player">>That's a depressing math problem.<</dialog>>
<<dialog "dinerWaitress1">>Welcome to waitressing.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"needing_vacation": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-10.webp">>
<<image _img "100%">>
<<narrative>>Emma stares out the window at the street, coffee cup in hand, not really seeing anything.<</narrative>>
<<dialog "dinerWaitress1">>I forgot what a day off feels like.<</dialog>>
<<dialog "player">>When's the last time you had one?<</dialog>>
<<dialog "dinerWaitress1">>Don't make me count. It'll make it worse.<</dialog>>
<<dialog "player">>You should take some time.<</dialog>>
<<dialog "dinerWaitress1">>Sure. I'll pencil that in between never and never.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-11.webp">>
<<image _img "100%">>
<<narrative>>Emma blows on her coffee, elbows on the counter, looking at the clock.<</narrative>>
<<dialog "dinerWaitress1">>Someone told me about a beach two hours from here.<</dialog>>
<<dialog "player">>That doesn't sound too far.<</dialog>>
<<dialog "dinerWaitress1">>Two hours feels like another planet right now.<</dialog>>
<<dialog "player">>Maybe after a slow week.<</dialog>>
<<dialog "dinerWaitress1">>There are no slow weeks.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-12.webp">>
<<image _img "100%">>
<<narrative>>Emma stretches her arms above her head, shoulders popping quietly.<</narrative>>
<<dialog "dinerWaitress1">>I keep saying I'll request time off.<</dialog>>
<<dialog "player">>And?<</dialog>>
<<dialog "dinerWaitress1">>And then I look at my bank account.<</dialog>>
<<dialog "player">>That'll do it.<</dialog>>
<<dialog "dinerWaitress1">>Every time.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_dishwasherOnBreak_afternoon": {
"venting_customer": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-7.webp">>
<<image _img "100%">>
<<narrative>>Emma drops into the seat at the end of the counter, rubbing her temple with two fingers.<</narrative>>
<<dialog "dinerWaitress1">>Guy at table three snapped his fingers at me.<</dialog>>
<<dialog "player">>Seriously? Like you're a dog?<</dialog>>
<<dialog "dinerWaitress1">>Exactly like that. Then complained I was slow.<</dialog>>
<<dialog "player">>What did you say?<</dialog>>
<<dialog "dinerWaitress1">>Smiled. Brought his food. Thought about quitting.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-8.webp">>
<<image _img "100%">>
<<narrative>>Emma leans against the counter, arms crossed, staring at a table in the corner.<</narrative>>
<<dialog "dinerWaitress1">>Lady over there changed her order three times.<</dialog>>
<<dialog "player">>Three times? James must love that.<</dialog>>
<<dialog "dinerWaitress1">>James doesn't know yet. I'm stalling.<</dialog>>
<<dialog "player">>Smart. Let her settle on something first.<</dialog>>
<<dialog "dinerWaitress1">>Or I just wait until she leaves.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-9.webp">>
<<image _img "100%">>
<<narrative>>Emma exhales hard through her nose, setting her notepad down on the counter like it personally wronged her.<</narrative>>
<<dialog "dinerWaitress1">>Some people think being polite is optional.<</dialog>>
<<dialog "player">>Bad table?<</dialog>>
<<dialog "dinerWaitress1">>Didn't say please once. Not once.<</dialog>>
<<dialog "player">>At least the tip might be decent.<</dialog>>
<<dialog "dinerWaitress1">>People like that don't tip.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"feet_hurting": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-10.webp">>
<<image _img "100%">>
<<narrative>>Emma sits on a stool behind the counter, one shoe half off, pressing her thumb into the sole of her foot.<</narrative>>
<<dialog "dinerWaitress1">>I swear this floor gets harder every day.<</dialog>>
<<dialog "player">>Maybe you need those padded mats.<</dialog>>
<<dialog "dinerWaitress1">>Told Vince. He said he'd look into it. Six months ago.<</dialog>>
<<dialog "player">>Classic management response.<</dialog>>
<<dialog "dinerWaitress1">>He's consistent, I'll give him that.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-11.webp">>
<<image _img "100%">>
<<narrative>>Emma rotates her ankles one at a time, leaning back against the counter with her eyes half closed.<</narrative>>
<<dialog "dinerWaitress1">>My calves are like concrete right now.<</dialog>>
<<dialog "player">>Afternoon shift that rough?<</dialog>>
<<dialog "dinerWaitress1">>Lunch rush plus cleanup plus afternoon tables. Yeah.<</dialog>>
<<dialog "player">>You make it look easy at least.<</dialog>>
<<dialog "dinerWaitress1">>That's the training. Suffer silently.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaFrontAfternoon-12.webp">>
<<image _img "100%">>
<<narrative>>Emma drums her fingers on the counter, looking down at her worn-out shoes with something close to contempt.<</narrative>>
<<dialog "dinerWaitress1">>These shoes owe me an apology.<</dialog>>
<<dialog "player">>How old are they?<</dialog>>
<<dialog "dinerWaitress1">>Older than they should be.<</dialog>>
<<dialog "player">>Time for new ones maybe.<</dialog>>
<<dialog "dinerWaitress1">>Time for a lot of things. Doesn't mean they happen.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysKitchen_dishwasherOnBreak_afternoon": {
"kitchen_complaint": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Emma leans against the pass window, picking at a bread roll. The kitchen hums behind her.<</narrative>>
<<dialog "dinerWaitress1">>Table five's been waiting twenty minutes.<</dialog>>
<<dialog "player">>Kitchen backed up?<</dialog>>
<<dialog "dinerWaitress1">>Always. I'm the one who gets the look though.<</dialog>>
<<dialog "player">>The customer look?<</dialog>>
<<dialog "dinerWaitress1">>The "where's my food" look. Yeah.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Emma checks tickets hanging on the rail, flipping through them with practiced fingers.<</narrative>>
<<dialog "dinerWaitress1">>Three orders wrong today. Three.<</dialog>>
<<dialog "player">>That's a lot. Miscommunication?<</dialog>>
<<dialog "dinerWaitress1">>My handwriting's fine. People just don't read.<</dialog>>
<<dialog "player">>You write the tickets by hand?<</dialog>>
<<dialog "dinerWaitress1">>Vince won't spring for a new system. So yeah.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Emma stands near the dishwasher area, sipping water from a paper cup, keeping out of the kitchen flow.<</narrative>>
<<dialog "dinerWaitress1">>It's hot as hell back here.<</dialog>>
<<dialog "player">>Yeah, I can feel it from the door.<</dialog>>
<<dialog "dinerWaitress1">>James doesn't even notice anymore. Says he runs hot.<</dialog>>
<<dialog "player">>Or he's just used to it.<</dialog>>
<<dialog "dinerWaitress1">>Same thing. Still miserable for the rest of us.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"coworker_venting": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Emma refills her water cup from the kitchen sink, back turned to the main line.<</narrative>>
<<dialog "dinerWaitress1">>Sofia called in late again. I covered her section.<</dialog>>
<<dialog "player">>Does she do that a lot?<</dialog>>
<<dialog "dinerWaitress1">>Define "a lot." Weekly? Yeah.<</dialog>>
<<dialog "player">>And nobody says anything?<</dialog>>
<<dialog "dinerWaitress1">>I say things. Just not to her face.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Emma leans on the stainless steel counter, arms folded, watching the kitchen work without being part of it.<</narrative>>
<<dialog "dinerWaitress1">>Jake's got energy for days. Makes me tired watching him.<</dialog>>
<<dialog "player">>He does seem pretty upbeat out there.<</dialog>>
<<dialog "dinerWaitress1">>Give him a year. That'll fade.<</dialog>>
<<dialog "player">>You were like that once?<</dialog>>
<<dialog "dinerWaitress1">>I don't remember. Probably not.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaKitchenAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Emma crumples her paper cup and tosses it in the trash, leaning against the doorframe.<</narrative>>
<<dialog "dinerWaitress1">>Tom's solid. Only one I trust with the register.<</dialog>>
<<dialog "player">>He seems pretty calm up front.<</dialog>>
<<dialog "dinerWaitress1">>That's why he works. Doesn't cause problems.<</dialog>>
<<dialog "player">>High praise coming from you.<</dialog>>
<<dialog "dinerWaitress1">>Don't tell him. He'll get a big head.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherOnBreak_afternoon": {
"guard_down": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Emma sits on a crate in the storage room, head tilted back against the wall, eyes closed. She opens one eye when you walk in.<</narrative>>
<<dialog "dinerWaitress1">>Oh. Hey. Didn't hear you come in.<</dialog>>
<<dialog "player">>Sorry. Didn't mean to interrupt your nap.<</dialog>>
<<dialog "dinerWaitress1">>Wasn't napping. Just... resting my eyes.<</dialog>>
<<dialog "player">>Sure. Resting your eyes. Very convincing.<</dialog>>
<<dialog "dinerWaitress1">>Shut up. You'd do the same after five hours out there.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Emma leans against a shelf of napkin boxes, holding a coffee cup with both hands like it's keeping her alive.<</narrative>>
<<dialog "dinerWaitress1">>This is the only quiet spot in the whole building.<</dialog>>
<<dialog "player">>It's nice back here. No customers.<</dialog>>
<<dialog "dinerWaitress1">>No customers, no orders, no fake smiling. Heaven.<</dialog>>
<<dialog "player">>The fake smiling part sounds rough.<</dialog>>
<<dialog "dinerWaitress1">>You have no idea. My face hurts by noon.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Emma is stretching her arms overhead, fingers laced together. She lets out a long breath when she drops them.<</narrative>>
<<dialog "dinerWaitress1">>Five more hours. I can do five more hours.<</dialog>>
<<dialog "player">>Sounds like you're convincing yourself.<</dialog>>
<<dialog "dinerWaitress1">>I am. Is it working?<</dialog>>
<<dialog "player">>Not really, no.<</dialog>>
<<dialog "dinerWaitress1">>Yeah. Didn't think so.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
"break_venting": [
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Emma pulls a stool out from behind some boxes and sits down, rubbing her knees.<</narrative>>
<<dialog "dinerWaitress1">>I used to think this job was temporary.<</dialog>>
<<dialog "player">>How long have you been here?<</dialog>>
<<dialog "dinerWaitress1">>Long enough to stop counting.<</dialog>>
<<dialog "player">>That's not an answer.<</dialog>>
<<dialog "dinerWaitress1">>It's the only one I've got.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Emma stares at the coffee machine in the corner of the break area. It gurgles weakly.<</narrative>>
<<dialog "dinerWaitress1">>This machine's been dying for a month.<</dialog>>
<<dialog "player">>Sounds like it's on its last legs.<</dialog>>
<<dialog "dinerWaitress1">>Just like me. We're bonding.<</dialog>>
<<dialog "player">>That's kind of sad and kind of funny.<</dialog>>
<<dialog "dinerWaitress1">>Story of my life, honestly.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/emma/level1/emmaStorageAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Emma sits with her elbows on her knees, staring at the floor. She looks up slowly when you come in.<</narrative>>
<<dialog "dinerWaitress1">>Do you ever just stand somewhere and forget why?<</dialog>>
<<dialog "player">>Yeah. More often than I'd like to admit.<</dialog>>
<<dialog "dinerWaitress1">>Happens to me three times a shift now.<</dialog>>
<<dialog "player">>Maybe you need more sleep.<</dialog>>
<<dialog "dinerWaitress1">>Maybe I need a different life. Sleep would help too though.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
}
}>>
<</nobr>>/* ==========================================
FAMILY MEALS WIDGETS
Widgets for eating with family system
Tracking: $familyMeals.breakfast/lunch/dinner
Reset daily via advanceDay widget
Meal Times (1 hour windows):
- Breakfast: 7:00 - 8:00
- Lunch: 12:00 - 13:00
- Dinner: 18:00 - 19:00
Quest Integration:
If a quest has mealType matching current meal, redirects to quest passage
========================================== */
/* Initialize family meals tracking if not exists */
<<widget "initFamilyMeals">><<silently>>
<<if ndef $familyMeals>>
<<set $familyMeals = { breakfast: false, lunch: false, dinner: false }>>
<</if>>
<</silently>><</widget>>
/* Reset family meals - call this at start of new day */
<<widget "resetFamilyMeals">><<silently>>
<<set $familyMeals = { breakfast: false, lunch: false, dinner: false }>>
<</silently>><</widget>>
/* Show Family Meal Button
Usage: <<showFamilyMealBtn>>
Shows button during meal times if family is present and meal not eaten yet
If a quest with matching mealType is active, redirects to quest passage
*/
<<widget "showFamilyMealBtn">><<silently>>
<<initFamilyMeals>>
/* Check if family member is at current location */
<<set _familyHere = $characters.mother.currentLocation is $location or $characters.father.currentLocation is $location or $characters.brother.currentLocation is $location>>
/* Determine current meal type (1 hour windows) */
<<set _isBreakfast = $timeSys.hour >= 7 and $timeSys.hour < 8>>
<<set _isLunch = $timeSys.hour >= 12 and $timeSys.hour < 13>>
<<set _isDinner = $timeSys.hour >= 18 and $timeSys.hour < 19>>
/* Set current meal type */
<<set _mealType = null>>
<<set _mealAvailable = false>>
<<if _isBreakfast and !$familyMeals.breakfast>>
<<set _mealType = "breakfast">>
<<set _mealAvailable = true>>
<<elseif _isLunch and !$familyMeals.lunch>>
<<set _mealType = "lunch">>
<<set _mealAvailable = true>>
<<elseif _isDinner and !$familyMeals.dinner>>
<<set _mealType = "dinner">>
<<set _mealAvailable = true>>
<</if>>
/* Check for active meal quest */
<<set _mealQuestPassage = null>>
<<if _mealType and $questState and $questState.active>>
<<for _qid, _state range $questState.active>>
<<set _quest = setup.quests[_qid]>>
<<if _quest and _quest.mealType is _mealType>>
<<set _stage = _quest.stages[_state.stage]>>
<<if _stage and _stage.passage>>
<<set _mealQuestPassage = _stage.passage>>
<</if>>
<</if>>
<</for>>
<</if>>
<</silently>><<if _familyHere and _mealAvailable and !_mealQuestPassage>><<btn "Eat with Family" "eatWithFamily">><</btn>><</if>><</widget>>
/* Check Family Meal Availability
Usage: <<canEatWithFamily>>
Sets _canEatWithFamily variable to true/false
*/
<<widget "canEatWithFamily">><<silently>>
<<initFamilyMeals>>
<<set _familyHere = $characters.mother.currentLocation is $location or $characters.father.currentLocation is $location or $characters.brother.currentLocation is $location>>
<<set _isBreakfast = $timeSys.hour >= 7 and $timeSys.hour < 8>>
<<set _isLunch = $timeSys.hour >= 12 and $timeSys.hour < 13>>
<<set _isDinner = $timeSys.hour >= 18 and $timeSys.hour < 19>>
<<set _mealAvailable = false>>
<<if _isBreakfast and !$familyMeals.breakfast>><<set _mealAvailable = true>>
<<elseif _isLunch and !$familyMeals.lunch>><<set _mealAvailable = true>>
<<elseif _isDinner and !$familyMeals.dinner>><<set _mealAvailable = true>>
<</if>>
<<set _canEatWithFamily = _familyHere and _mealAvailable>>
<</silently>><</widget>>/* ==========================================
FATHER TALK TOPICS POST-WORK, LEVEL 1
IMG MAPPING:
kitchenMorning: coffee=1, quickEat=2, briefCheck=3
kitchenEvening: dinner=1, debrief=2, checkIn=3
livingroomEvening: unwinding=1, tv=2, quiet=3
garageEvening: carwork=1, tools=2, radio=3
parentsRoomMorning: leaving=1, preparing=2, moment=3
parentsRoomEvening: windingDown=1, nightCheck=2, reflection=3
========================================== */
<<set setup.fatherTopics = setup.fatherTopics || {}>>
<<set setup.fatherTopics.postWork = setup.fatherTopics.postWork || {}>>
<<set setup.fatherTopics.postWork.level1 = {
fhKitchen_postWork_morning: {
coffee: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's already dressed and moving fast. Commute mode. He glances up from his coffee as you come in.<</narrative>>
<<dialog "father">>Morning. You're up early.<</dialog>>
<<dialog "player">>Couldn't sleep.<</dialog>>
<<dialog "father">>(pouring a second mug) Coffee's hot.<</dialog>>
<<dialog "player">>Thanks. Long day ahead?<</dialog>>
<<dialog "father">>(checking his watch) Always is.
(draining his mug)
Right. I'll see you tonight.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's eating toast standing up. Proper sit-down breakfast is a thing of the past now.<</narrative>>
<<dialog "father">>(eating over the counter) Meeting at nine. I need to leave in ten.
(glancing at you)
Did you need something?<</dialog>>
<<dialog "player">>No, just getting coffee.<</dialog>>
<<dialog "father">>(relaxing slightly) Right. Okay.
(a pause)
Good morning.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's running a few minutes early, which is unusual, and he actually has a moment.<</narrative>>
<<dialog "father">>I've got five minutes. Sit if you want.<</dialog>>
<<dialog "player">>(sitting) Early start?<</dialog>>
<<dialog "father">>Covering for someone. It's fine.
(briefly human)
How are you?<</dialog>>
<<dialog "player">>Fine. Not bad.<</dialog>>
<<dialog "father">>(nodding, finishing his coffee) Good.
(standing)
I'll see you tonight.<</dialog>>`,
friendship: 2, trust: 1
}
],
quickEat: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's trying to eat toast while finding his keys.<</narrative>>
<<dialog "father">>Have you seen my never mind.
(finding them in his jacket)
Right. Keys.<</dialog>>
<<dialog "player">>Do you want me to make you something proper?<</dialog>>
<<dialog "father">>No time.
(toast in hand)
This is fine.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He notices you've already put the kettle on. He looks briefly pleased.<</narrative>>
<<dialog "father">>(quietly) Thanks for that.
(pouring)
I didn't ask you to do that.<</dialog>>
<<dialog "player">>I was making mine anyway.<</dialog>>
<<dialog "father">>Still.
(a shorter pause than you'd expect)
Appreciated.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He pauses at the door, bag in hand, and looks back into the kitchen.<</narrative>>
<<dialog "father">>Don't wait on lunch for me. I'll eat at work.
(meeting your eye briefly)
Look after your mother.<</dialog>>
<<dialog "player">>I always do.<</dialog>>
<<dialog "father">>(a slight nod) I know.
(going)<</dialog>>`,
friendship: 2, trust: 1
}
],
briefCheck: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He glances at the kitchen calendar as he passes. A habit now.<</narrative>>
<<dialog "father">>Jake got the early bus?<</dialog>>
<<dialog "player">>Think so. I heard the door.<</dialog>>
<<dialog "father">>(satisfied) Good.
(grabbing his bag)
Your mother up?<</dialog>>
<<dialog "player">>Not yet.<</dialog>>
<<dialog "father">>(quieter) Let her sleep.
(heading out)<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He pauses just before leaving, like he thought of something.<</narrative>>
<<dialog "father">>If anything comes up today anything the house needs let me know. I'll sort it tonight.<</dialog>>
<<dialog "player">>Will do.<</dialog>>
<<dialog "father">>Good.
(at the door now)
See you later.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's at the counter with a rare unhurried moment. Someone cancelled a meeting.<</narrative>>
<<dialog "father">>First meeting's been moved. I've got fifteen minutes.
(sitting down, actually)
Tell me something that's happening here while I'm gone.<</dialog>>
<<dialog "player">>(thinking) Mom found a good bakery nearby. Two streets down.<</dialog>>
<<dialog "father">>Is it good?<</dialog>>
<<dialog "player">>Really good, apparently.<</dialog>>
<<dialog "father">>(nodding) Good. I'll try it at the weekend.
(checking his watch)
Right. I've got to go.<</dialog>>`,
friendship: 2
}
]
},
fhKitchen_postWork_evening: {
dinner: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He comes in from work, tie loosened before he's even in the door. He goes straight to the sink.<</narrative>>
<<dialog "father">>(washing his hands) Smells good.<</dialog>>
<<dialog "player">>Mom made that pasta thing.<</dialog>>
<<dialog "father">>(drying his hands) The one with the capers?<</dialog>>
<<dialog "player">>Think so.<</dialog>>
<<dialog "father">>(quietly pleased) Good.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He sets the table. He's started doing it without being asked since work started.<</narrative>>
<<dialog "father">>(working) How was the day?<</dialog>>
<<dialog "player">>Fine. You?<</dialog>>
<<dialog "father">>Long.
(folding a napkin precisely)
Fine, though.
(a pause)
We had a decent result on the thing I've been working on.<</dialog>>
<<dialog "player">>That's good.<</dialog>>
<<dialog "father">>(briefly, genuinely) Yeah. It was.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He sits down to dinner looking hollowed out but functional.<</narrative>>
<<dialog "father">>I forgot what traffic was like.
(reaching for water)
Forty-five minutes for twenty minutes of road.<</dialog>>
<<dialog "player">>Every day?<</dialog>>
<<dialog "father">>Most days. I'll figure out better timing.
(taking a breath, letting it go)
Anyway. This looks good.<</dialog>>`,
friendship: 1
}
],
debrief: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He rinses his plate and leans on the counter for a moment. Processing the day.<</narrative>>
<<dialog "father">>New team's not bad. Bit slow to start. They'll get there.
(to himself)
They always do.<</dialog>>
<<dialog "player">>Hard to settle in at first.<</dialog>>
<<dialog "father">>(looking at you) Yeah. I know exactly what you mean.
(a pause)
Goes for all of us.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He pours water for both of them without thinking. The new evening habit.<</narrative>>
<<dialog "father">>Ask me about my day.<</dialog>>
<<dialog "player">>(raising an eyebrow) ...How was your day?<</dialog>>
<<dialog "father">>(almost smiling) Productive.
(handing you the glass)
Thanks for asking.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He checks something on his phone, then puts it face-down. A conscious act.<</narrative>>
<<dialog "father">>Done with that until tomorrow.
(firmly)
That's the rule. After six-thirty, it goes down.<</dialog>>
<<dialog "player">>Good rule.<</dialog>>
<<dialog "father">>It's a necessary rule.
(looking at you)
Your generation's better at that than mine, I think.<</dialog>>`,
friendship: 2
}
],
checkIn: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He does the washing up while you dry. The pattern they've settled into.<</narrative>>
<<dialog "father">>Jake alright today?<</dialog>>
<<dialog "player">>Seemed good. He watched a film with us in the afternoon.<</dialog>>
<<dialog "father">>(quiet satisfaction) Good. Good.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's preparing tomorrow's lunch the night before. Systematic.<</narrative>>
<<dialog "father">>How's your mother been today?<</dialog>>
<<dialog "player">>Good. She found a new thing she wants to do with the garden.<</dialog>>
<<dialog "father">>(quietly) Good. She needs projects.
(to himself)
So do I.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He pauses mid-task and looks at you properly. Rare, after a long day.<</narrative>>
<<dialog "father">>You alright? Genuinely.<</dialog>>
<<dialog "player">>Yeah. I'm fine.<</dialog>>
<<dialog "father">>(studying you briefly) Okay. You'd say if you weren't?<</dialog>>
<<dialog "player">>Probably.<</dialog>>
<<dialog "father">>(going back to what he was doing) Close enough.<</dialog>>`,
friendship: 2, trust: 1
}
]
},
fhLivingroom_postWork_evening: {
unwinding: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's in the armchair, jacket off, shoes off. The decompress ritual in full effect.<</narrative>>
<<dialog "father">>(flatly) Don't talk to me for ten minutes.<</dialog>>
<<dialog "player">>Okay.<</dialog>>
<<narrative>>You sit quietly. After about eight minutes:<</narrative>>
<<dialog "father">>Alright.
(slightly better)
Good day?<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's got his eyes closed but he's not sleeping. Thinking, or not thinking. Hard to tell.<</narrative>>
<<dialog "player">>Do you want anything? Tea?<</dialog>>
<<dialog "father">>(without opening his eyes) That'd be good. Yes.
(a beat)
Thank you.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's switched on the TV but isn't watching it. Just noise cover while he sits.<</narrative>>
<<dialog "father">>I'm not tired. Just done.
(without looking at you)
There's a difference.<</dialog>>
<<dialog "player">>I know the difference.<</dialog>>
<<dialog "father">>(glancing over) Yeah. I thought you might.<</dialog>>`,
friendship: 2, trust: 1
}
],
tv: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>Something on TV catches him off guard. He actually laughs. Short and genuine.<</narrative>>
<<dialog "player">>What was that?<</dialog>>
<<dialog "father">>(still smiling) I don't know. But it was the right thing at the right time.
(gesturing to the couch)
Sit down. This was good.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He mutes an advert and turns to you like he's been waiting for a quiet moment.<</narrative>>
<<dialog "father">>Your mother mentioned you might need something. A thing for the room shelving?<</dialog>>
<<dialog "player">>It's not urgent.<</dialog>>
<<dialog "father">>I'll sort it this weekend.
(unmuting the TV)
I've got the hardware anyway.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He turns the TV off unexpectedly. Just sits in the quiet.<</narrative>>
<<dialog "father">>I've been watching screens all day. Can't face another one.
(glancing at you)
You don't have to sit here.<</dialog>>
<<dialog "player">>I don't mind.<</dialog>>
<<dialog "father">>(settling back) Okay then.<</dialog>>`,
friendship: 2
}
],
quiet: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>You're sitting in the same room. Him doing nothing, you reading or scrolling. It's comfortable.<</narrative>>
<<dialog "father">>This is actually nice.
(just)
That.<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "father">>(that's enough for him)<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He looks at the family photo on the shelf. The one he put up himself.<</narrative>>
<<dialog "father">>That photo's been in three different houses now.
(quietly)
Good photo.<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "father">>(back to doing nothing) Right.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He says goodnight before you do. Unusual. Normally you head up first.<</narrative>>
<<dialog "father">>Right. I'm going up.
(passing you)
Goodnight.<</dialog>>
<<dialog "player">>Night.<</dialog>>
<<dialog "father">>(pausing) You'll lock up?<</dialog>>
<<dialog "player">>I've got it.<</dialog>>
<<dialog "father">>(nodding) Good.
(heading upstairs)<</dialog>>`,
friendship: 2
}
]
},
fhGarage_postWork_evening: {
carwork: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's under the bonnet. Work clothes still on, or a t-shirt if he changed. He doesn't look up right away.<</narrative>>
<<dialog "father">>Hand me the long socket. Third drawer.<</dialog>>
<<dialog "player">>(finding it) This?<</dialog>>
<<dialog "father">>(taking it) Yeah. Thanks.
(working)
You don't have to stay.<</dialog>>
<<dialog "player">>It's fine. I'll watch.<</dialog>>
<<dialog "father">>(a small shift in posture he doesn't mind) Suit yourself.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's checking the tyre pressure. Methodical, slow. Not in a hurry to be anywhere.<</narrative>>
<<dialog "father">>Know how to check these?<</dialog>>
<<dialog "player">>Not really.<</dialog>>
<<dialog "father">>Come here. I'll show you once. You'll want to know it eventually.
(positioning the gauge)
Basic thing. Should take thirty seconds.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He steps back from the car and wipes his hands. Done for the night. Satisfied.<</narrative>>
<<dialog "father">>That's the cooling system sorted. Took three evenings.
(simply)
Worth it though.<</dialog>>
<<dialog "player">>Do you like this? The car stuff?<</dialog>>
<<dialog "father">>(considering) Yeah. Yeah, I do.
(a pause)
It's the only thing I work on where I can actually see it's done.<</dialog>>`,
friendship: 2, trust: 1
}
],
tools: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's cleaning tools at the workbench. Precise, habitual. Clearly therapeutic.<</narrative>>
<<dialog "father">>You can sit there if you want.
(indicating a stool)
I'm not going to talk much.<</dialog>>
<<dialog "player">>(sitting) That's fine.<</dialog>>
<<dialog "father">>(working quietly for a while) Good day for you?<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "father">>(brief nod, back to the tools) Good.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He hands you a wire brush and nods at a part on the bench.<</narrative>>
<<dialog "father">>Clean that up if you're just standing there.<</dialog>>
<<dialog "player">>(taking it) What am I cleaning?<</dialog>>
<<dialog "father">>Rust on the bracket. Circular motion.
(going back to his own work)
Not too hard.<</dialog>>
<<narrative>>You work alongside him quietly. It's strangely easy.<</narrative>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He notices you looking at something on the wall. Old tools, framed like display pieces.<</narrative>>
<<dialog "father">>My grandfather's. He was a machinist.
(without stopping what he's doing)
Good hands. Better than mine, probably.<</dialog>>
<<dialog "player">>Do you wish you'd done something like that? With your hands?<</dialog>>
<<dialog "father">>(thinking) Sometimes.
(back to the task)
Computers pay better.<</dialog>>`,
friendship: 2, trust: 1
}
],
radio: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's got the radio on. Same station as always. He doesn't talk over it.<</narrative>>
<<dialog "player">>What is this?<</dialog>>
<<dialog "father">>Late night radio. Talk show.
(listening)
Guy knows what he's talking about, for once.<</dialog>>
<<dialog "player">>High praise.<</dialog>>
<<dialog "father">>(a short exhale almost a laugh) Very rare. Don't get used to me saying it.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He turns it down when a song comes on that irritates him.<</narrative>>
<<dialog "father">>(mildly) That's a bad song.<</dialog>>
<<dialog "player">>It's not great.<</dialog>>
<<dialog "father">>(satisfied to agree) No. It isn't.
(turning it to background level)
How does that get on the radio.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/garageEvening-3.webp">>
<<image _img "100%">>
<<narrative>>Something plays that he actually knows. He drums once on the workbench, then stops.<</narrative>>
<<dialog "player">>Was that did you just?<</dialog>>
<<dialog "father">>(returning to work, straight face) I don't know what you're talking about.<</dialog>>
<<dialog "player">>(grinning) You drummed on the bench.<</dialog>>
<<dialog "father">>(pause) I was checking the surface for resonance.<</dialog>>`,
friendship: 2
}
]
},
fhParentsRoom_postWork_morning: {
leaving: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's doing his tie in the mirror. Almost ready. Commute mode.<</narrative>>
<<dialog "father">>(without turning) You need something?<</dialog>>
<<dialog "player">>No. Just passing.<</dialog>>
<<dialog "father">>(straightening his tie) I'm leaving in five. If anything needs doing today call me, I'll sort it tonight.<</dialog>>
<<dialog "player">>Okay.<</dialog>>
<<dialog "father">>(grabbing his bag) Good.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's looking for something. Checking pockets, desk, jacket.<</narrative>>
<<dialog "father">>Have you seen my phone? It was
(finds it on the nightstand)
Never mind.
(exhales briefly)
I need to stop leaving it there.<</dialog>>
<<dialog "player">>It's always there.<</dialog>>
<<dialog "father">>(heading out) I know. That's the problem.
(calling back from the hall)
Goodbye.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He pauses at the door. Something he wanted to say. He's not sure if there's time.<</narrative>>
<<dialog "father">>(deciding there is) Look after your mother today. She's been quiet.
(not waiting for a response, heading out)
I'll be back by seven.<</dialog>>
<<dialog "player">>I'll keep an eye out.<</dialog>>
<<dialog "father">>(from the stairs, lower) I know you will.<</dialog>>`,
friendship: 1, trust: 1
}
],
preparing: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's laying out tomorrow's clothes the night before. Old habit from work, still going.<</narrative>>
<<dialog "player">>(at the door) You do that every night?<</dialog>>
<<dialog "father">>(folding his jacket) Saves time in the morning. When you're half-asleep and making decisions, you make bad ones.<</dialog>>
<<dialog "player">>Makes sense.<</dialog>>
<<dialog "father">>(drily) Learnt it the hard way. Showed up to a client meeting in mismatched socks once.
(back to the task)
Never again.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's checking his bag. Methodical. Making sure everything's there.<</narrative>>
<<dialog "father">>Do you know if there's anything happening this weekend? Your mother mentioned something.<</dialog>>
<<dialog "player">>I don't think so. Why?<</dialog>>
<<dialog "father">>I want to get the bathroom shelf sorted. I need a clear run at it.
(zipping his bag)
I'll check with her.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He finds a note he wrote himself. Reads it. Nods, and pockets it.<</narrative>>
<<dialog "player">>What's that?<</dialog>>
<<dialog "father">>Reminder. I have a habit of thinking of things at 6am and then forgetting them by 8.
(pocketing it)
So I write them down immediately.<</dialog>>
<<dialog "player">>Does it work?<</dialog>>
<<dialog "father">>(a slight pause) Better than not doing it.
(picking up his bag)
Good morning.<</dialog>>`,
friendship: 2
}
],
moment: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's almost out when he catches you in the doorway. He turns back with a quiet look.<</narrative>>
<<dialog "father">>I heard you up late last night.
(not accusatory)
You alright?<</dialog>>
<<dialog "player">>Yeah. Just couldn't sleep.<</dialog>>
<<dialog "father">>Okay.
(holding your gaze a second)
Tell me if there's something.
(heading out)<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He looks back as he leaves. Not dramatic. A check.<</narrative>>
<<dialog "father">>See you tonight.
(meeting your eye briefly)
It's a good day. I can tell.<</dialog>>
<<dialog "player">>How?<</dialog>>
<<dialog "father">>(already going, quietly) I don't know. I just can.
(from the hallway)
See you later.<</dialog>>`,
friendship: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He passes you on the way out and pauses. He's decided to say something.<</narrative>>
<<dialog "father">>You've been good this week. With the house, with your mother.
(simply)
I notice, even when I don't say it.<</dialog>>
<<dialog "player">>(quietly) Thanks, Dad.<</dialog>>
<<dialog "father">>(that word brief, absorbed)
Good.
(heading out)<</dialog>>`,
friendship: 2, trust: 1, love: 1
}
]
},
fhParentsRoom_postWork_evening: {
windingDown: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's sitting on the bed with his shoes off, doing nothing. Completely still. The last bit of decompression.<</narrative>>
<<dialog "player">>You okay?<</dialog>>
<<dialog "father">>(not moving) Fine. Just done.
(after a moment)
Good kind of done. Not bad done.<</dialog>>
<<dialog "player">>Okay.<</dialog>>
<<dialog "father">>(looking at you) Anything happen today I should know about?<</dialog>>
<<dialog "player">>Nothing serious.<</dialog>>
<<dialog "father">>(nodding, satisfied) Good. Goodnight then.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's changed out of work clothes. He's in that specific released state that only comes after.<</narrative>>
<<dialog "father">>(quietly) Long day.
(not complaining, more reporting)
Good work, just long.<</dialog>>
<<dialog "player">>Do you want anything? Tea?<</dialog>>
<<dialog "father">>(thinking) No. I'm going to sleep.
(a pause)
That's a kindness though. Thank you.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's already lying down but not asleep. Staring at the ceiling. Thinking.<</narrative>>
<<dialog "player">>(in the doorway) Sorry did I wake you?<</dialog>>
<<dialog "father">>(eyes still closed) No.
(opening them)
What do you need?<</dialog>>
<<dialog "player">>Nothing. Just checking.<</dialog>>
<<dialog "father">>(the faintest appreciation in that) I'm fine.
(closing his eyes)
Goodnight.<</dialog>>`,
friendship: 2, trust: 1
}
],
nightCheck: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's reading before bed. The short window he gives himself before lights out.<</narrative>>
<<dialog "father">>(without looking up) Jake settled?<</dialog>>
<<dialog "player">>Yeah. He's in his room.<</dialog>>
<<dialog "father">>(turning a page) Good.
(a beat)
Your mother?<</dialog>>
<<dialog "player">>She went to bed an hour ago.<</dialog>>
<<dialog "father">>(satisfied, still reading) Good.
(a longer pause, then)
Thank you.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's almost asleep but catches you at the door.<</narrative>>
<<dialog "father">>(low) Hey.<</dialog>>
<<dialog "player">>Yeah?<</dialog>>
<<dialog "father">>You're locking up?<</dialog>>
<<dialog "player">>Already did.<</dialog>>
<<dialog "father">>(settling back) Good.
(almost gone)
Goodnight.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He looks up from his book and catches you in the doorway. Properly looks at you.<</narrative>>
<<dialog "father">>Come in a second.
(putting the book down)
You look like you're thinking about something.<</dialog>>
<<dialog "player">>Just nothing. Long day.<</dialog>>
<<dialog "father">>(studying you briefly) Okay.
(a pause)
I know I'm not always the easiest to talk to. But you can. If you need to.
(back to his book)
Just so you know.<</dialog>>`,
friendship: 2, trust: 2
}
],
reflection: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's sitting on the edge of the bed, not in motion, not heading anywhere. He looks up when you appear.<</narrative>>
<<dialog "father">>I've been thinking about something all day.
(matter-of-fact)
Not a problem. Just a thing.<</dialog>>
<<dialog "player">>What kind of thing?<</dialog>>
<<dialog "father">>Whether this is working. The whole picture.
(looking at you directly)
I think it is. I just like to check.<</dialog>>
<<dialog "player">>I think it's working.<</dialog>>
<<dialog "father">>(a single nod that settled it) Good. Goodnight.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's looking at the ceiling the way people do when they're working something out.<</narrative>>
<<dialog "player">>(quietly) Okay?<</dialog>>
<<dialog "father">>Yeah.
(a beat)
I had a conversation at work today. With a colleague. She said something that stuck with me.
(pause)
About not saying things out loud until you can't unsay them.
(looking at you)
I think I do that too much. Keep things in.<</dialog>>
<<dialog "player">>You're saying it now.<</dialog>>
<<dialog "father">>(quietly) Trying to.
(turning the lamp off)
Goodnight.<</dialog>>`,
friendship: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level1/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's looking upward, thinking. He speaks without turning his head.<</narrative>>
<<dialog "father">>You know what I did today that I don't normally do?
(not waiting)
I told someone at work they did a good job. Just said it. Straight out.
(a rueful pause)
Apparently that's unusual for me.<</dialog>>
<<dialog "player">>(quietly amused) Yes. It is.<</dialog>>
<<dialog "father">>(accepting this) Right. Well.
(turning the lamp off)
I'm working on it. Goodnight.<</dialog>>`,
friendship: 2, trust: 1, love: 1
}
]
}
}>>/* ==========================================
FATHER TALK TOPICS POST-WORK, LEVEL 2
IMG MAPPING:
kitchenMorning: lookingOut=1, morningChat=2, handoff=3
kitchenEvening: openingUp=1, humour=2, memory=3
livingroomEvening: bonding=1, advice=2, quiet=3
garageEvening: teaching=1, stories=2, realTalk=3
parentsRoomMorning: leaving=1, preparing=2, moment=3
parentsRoomEvening: vulnerable=1, nightRoutine=2, closing=3
========================================== */
<<set setup.fatherTopics = setup.fatherTopics || {}>>
<<set setup.fatherTopics.postWork = setup.fatherTopics.postWork || {}>>
<<set setup.fatherTopics.postWork.level2 = {
fhKitchen_postWork_morning: {
lookingOut: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's already set a mug out for you. Didn't say anything. Just did it.<</narrative>>
<<dialog "player">>Oh. Thanks.<</dialog>>
<<dialog "father">>(at the stove) You're usually up by this time.
(briefly)
I figured.<</dialog>>
<<dialog "player">>You noticed my schedule?<</dialog>>
<<dialog "father">>(pouring his coffee) I notice most things.
(heading out)
See you tonight.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He catches you looking tired and doesn't comment on whatever he was about to say.<</narrative>>
<<dialog "father">>(quieter than usual) Long night?<</dialog>>
<<dialog "player">>Couldn't sleep. It's fine.<</dialog>>
<<dialog "father">>(pause) There are those earplugs in the bathroom cabinet. Second shelf.
(leaving)
The house makes noise in the night. You get used to it.<</dialog>>`,
friendship: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He slides a folded note across the counter on his way out.<</narrative>>
<<dialog "player">>What's this?<</dialog>>
<<dialog "father">>(almost at the door) Number for the boiler company. If it makes that noise again while I'm out, call them direct. I already explained the situation. They'll come same day.
(leaving)
I should've written it down sooner.<</dialog>>`,
friendship: 3, trust: 2
}
],
morningChat: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He has a few unexpected minutes and doesn't immediately fill them with his phone.<</narrative>>
<<dialog "father">>(sitting) I used to hate mornings at work. Had to train myself out of it.
(to you)
What are you like in the morning? You're a morning person?<</dialog>>
<<dialog "player">>Getting there.<</dialog>>
<<dialog "father">>Ha.
(quietly amused)
Me too. Still.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's putting his lunch together. The now-familiar morning ritual.<</narrative>>
<<dialog "father">>If you're going past the hardware place today, I left a list on the side. If not, no problem.<</dialog>>
<<dialog "player">>I can go. What do you need?<</dialog>>
<<dialog "father">>(looking up) You don't have to. I just thought if you were going anyway<</dialog>>
<<dialog "player">>I'll go. It's fine.<</dialog>>
<<dialog "father">>(a beat, genuinely pleased) Okay. Thank you.
(adding to the list)
I'll add the sizes I need.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He pauses before leaving. Eye contact. Intentional.<</narrative>>
<<dialog "father">>Have a good day.
(simply, sincerely)
Not just automatically. I mean it.<</dialog>>
<<dialog "player">>(a beat) You too.<</dialog>>
<<dialog "father">>(nods, goes)<</dialog>>`,
friendship: 3, love: 1
}
],
handoff: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He passes you on the way out, shoulder barely touching yours, and keeps going.<</narrative>>
<<dialog "father">>Heard you last night, by the way. Fixing the thing with the cupboard door.
(without stopping)
Good fix.<</dialog>>
<<dialog "player">>I used the wedge from the toolbox.<</dialog>>
<<dialog "father">>(already gone, from the hallway) Exactly right.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He drops his keys on the counter and picks them up. A thought caught mid-motion.<</narrative>>
<<dialog "father">>I've cleared Saturday morning. We can finish the garage properly.
(to you)
If you're free.<</dialog>>
<<dialog "player">>Yeah, I'm free.<</dialog>>
<<dialog "father">>(that settled) Good. We'll do it properly.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's halfway out when a thought catches him. He comes back in briefly.<</narrative>>
<<dialog "father">>That book on the hallway shelf. I left it there for you.
(no preamble)
It's not exciting but it is useful. Engineering principles. Practical stuff.
(already going)
You don't have to read it. But you'd find it interesting.<</dialog>>`,
friendship: 3, trust: 1
}
]
},
fhKitchen_postWork_evening: {
openingUp: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He sits at the kitchen table before dinner's ready. Unusual. He normally moves around.<</narrative>>
<<dialog "father">>Kid started today. My team. Mid-twenties, just finished his degree.
(looking at the table)
Reminds me of how much I didn't know at that age.
(to you)
I probably made it harder on him than I needed to today.<</dialog>>
<<dialog "player">>You noticed that?<</dialog>>
<<dialog "father">>(wryly) I'm not totally without self-awareness.
(getting up)
I'll do better tomorrow.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He talks while setting the table. The kind of talking where he's working something out.<</narrative>>
<<dialog "father">>There's a project I've been asked to lead. Large one. More than I've done before.
(placing a fork)
I should say yes. I've said yes to things I wasn't ready for before and it worked out.
(pause)
I always find it hard to be sure.<</dialog>>
<<dialog "player">>That sounds like you should take it.<</dialog>>
<<dialog "father">>(briefly looking at you) Yeah. I think you're right.
(back to the table)
I probably knew that already.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He catches you mid-something and asks, genuinely curious.<</narrative>>
<<dialog "father">>What are you working on?<</dialog>>
<<dialog "player">>(explaining)<</dialog>>
<<dialog "father">>(listening, then) Hm. Sounds like you know what you're doing.<</dialog>>
<<dialog "player">>Still figuring it out.<</dialog>>
<<dialog "father">>(quietly) So am I. Most of the time.
(going back to the stove)
That's not a bad thing.<</dialog>>`,
friendship: 3, trust: 2
}
],
humour: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He tastes whatever's on the stove and makes a face. Then immediately tastes it again.<</narrative>>
<<dialog "player">>Is that good or bad?<</dialog>>
<<dialog "father">>(tasting a third time) Good. I'm just making sure.
(looking at you)
I'm thorough.<</dialog>>
<<dialog "player">>You just like tasting things.<</dialog>>
<<dialog "father">>(pause) ...Both can be true.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's been quiet, then:<</narrative>>
<<dialog "father">>I was in a meeting today. Very important, apparently.
(drily)
Forty-five minutes about a font choice.<</dialog>>
<<dialog "player">>...The font?<</dialog>>
<<dialog "father">>The font. I said nothing. I just watched it happen.
(back to clearing up)
I feel that I grew as a person today.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He walks into the kitchen and stops. Looks at the sink.<</narrative>>
<<dialog "father">>Did you fix the tap?<</dialog>>
<<dialog "player">>It was dripping.<</dialog>>
<<dialog "father">>(examining it) How did you know to do that?<</dialog>>
<<dialog "player">>You explained it. When you did the other one.<</dialog>>
<<dialog "father">>(quiet for a moment, something warm in it) You paid attention.
(back to normal)
Good.<</dialog>>`,
friendship: 3, trust: 2, love: 1
}
],
memory: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He sees something in the kitchen that triggers a memory. Holds it a moment, then speaks.<</narrative>>
<<dialog "father">>My mother used to make something that smelled like that. Same thing, probably.
(quietly)
Every house I've been in that's reminded me of hers has felt like home faster.
(without sentiment, almost a fact)
This one did. Quickly.<</dialog>>
<<dialog "player">>Yeah?<</dialog>>
<<dialog "father">>(brief) Yeah.
(back to normal)<</dialog>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's at the counter and says something without warning:<</narrative>>
<<dialog "father">>When you were very small, maybe three, you used to follow me around the kitchen and hand me things. Wrong things, usually.
(a small, genuine smile)
Confident about it, though.<</dialog>>
<<dialog "player">>I had no idea what you were doing.<</dialog>>
<<dialog "father">>(nodding) No. But you were there.
(back to what he was doing)
That was good.<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He hands you a mug. Deliberate. Paired with something he wanted to say.<</narrative>>
<<dialog "father">>We've moved three times since you were a child.
(sitting with his own mug)
I've always worried we disrupted things too much. For you and Jake.
(looking at you)
But you've both turned out you've both been
(stopping, not being maudlin)
Fine. You've turned out fine.<</dialog>>
<<dialog "player">>(quietly) We have.<</dialog>>
<<dialog "father">>(nodding, satisfied) Good.<</dialog>>`,
friendship: 3, trust: 2, love: 2
}
]
},
fhLivingroom_postWork_evening: {
bonding: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's watching something and clearly enjoying it more because you're there. He says nothing about this.<</narrative>>
<<dialog "father">>(during a quiet bit) I almost didn't put this on. Glad I did.
(glancing at you)
Better with someone.<</dialog>>
<<dialog "player">>Yeah. It is.<</dialog>>
<<dialog "father">>(settling back, easy in it)<</dialog>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He asks something out of nowhere:<</narrative>>
<<dialog "father">>What's something you're good at that I don't know about?<</dialog>>
<<dialog "player">>(surprised) Where did that come from?<</dialog>>
<<dialog "father">>I was thinking earlier.
(waiting)
Go on.<</dialog>>
<<dialog "player">>(answering)<</dialog>>
<<dialog "father">>(considering it) I didn't know that.
(with something close to pride)
That's good.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's sitting in his chair. You're nearby. He looks over at some point and says:<</narrative>>
<<dialog "father">>You know I'm not great at talking generally.
(plainly)
But I mean the things I don't say as much as what I do.
(back to the TV)
That's probably obvious. Just wanted to say it once.<</dialog>>
<<dialog "player">>I know, Dad.<</dialog>>
<<dialog "father">>(a nod. That's enough.)<</dialog>>`,
friendship: 3, love: 2, trust: 2
}
],
advice: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He offers something. Not preachy. More like a note he wanted to pass along.<</narrative>>
<<dialog "father">>I've worked with a lot of people who were afraid to ask questions. Thought it made them look slow.
(to you)
It doesn't. Questions are how you actually get fast.
(muting the TV for this)
Ask the dumb question. Always.<</dialog>>
<<dialog "player">>Speaking from experience?<</dialog>>
<<dialog "father">>(slight smile) Painful experience.
(unmuting)
You're welcome.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>You mention something you're worried about. He doesn't rush his answer.<</narrative>>
<<dialog "father">>(after a beat) That's a real problem. And it's a solvable problem.
(thinking)
Here's how I'd approach it. You can ignore this entirely.
(laying it out calmly)
But the first step is probably just...<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He gives advice he clearly followed himself:<</narrative>>
<<dialog "father">>Put the boring things on a list. Don't keep them in your head. They take up more space in your head than on paper.
(tapping his temple)
This should be for the actual thinking, not for remembering what you need to do.<</dialog>>
<<dialog "player">>Your notepad rule.<</dialog>>
<<dialog "father">>(mildly pleased you noticed) Exactly. Fifteen years and counting.<</dialog>>`,
friendship: 3
}
],
quiet: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He falls asleep in the chair. Actually, properly asleep midway through a documentary. Wakes up ten minutes later.<</narrative>>
<<dialog "father">>(groggily) Where did it get to?<</dialog>>
<<dialog "player">>(quietly) I paused it.<</dialog>>
<<dialog "father">>(finding the remote) Oh.
(genuinely)
Thank you.
(pressing play, not mentioning he was asleep)<</dialog>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's going to bed and pauses in the doorway.<</narrative>>
<<dialog "father">>Good company tonight.
(low, not making a big thing of it)
Goodnight.<</dialog>>
<<dialog "player">>Night.<</dialog>>
<<narrative>>He goes. The room's quieter now. In the good way.<</narrative>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's in the middle of reading his book and lowers it to look at you.<</narrative>>
<<dialog "father">>You should read this when I'm done. You'd like it.
(back to reading)
Not telling you anything more, that'd ruin it.<</dialog>>
<<dialog "player">>What kind of book is it?<</dialog>>
<<dialog "father">>(eyes still on the page) The kind you should read.
(a beat)
Trust me.<</dialog>>`,
friendship: 3
}
]
},
fhGarage_postWork_evening: {
teaching: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He sets down what he's doing and turns. Tonight he came to teach, not just work.<</narrative>>
<<dialog "father">>Right. I've been thinking about what's actually useful for you to know.
(picking up a part)
If anything with the car breaks when I'm not around, and it will eventually, you should be able to diagnose it yourself.
(looking at you)
That's the lesson tonight. Diagnosis. Not fixing. Just knowing what you're looking at.<</dialog>>
<<dialog "player">>Okay.<</dialog>>
<<dialog "father">>Good. Start here.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He walks you through something. Not impatient. Actually taking time with it.<</narrative>>
<<dialog "father">>The older I get the more I think teaching something is the best way to understand it yourself.
(showing you a joint)
So for my benefit as much as yours. Here's how this works.
(explaining it properly)<</dialog>>
<<dialog "player">>(listening, looking)<</dialog>>
<<dialog "father">>(checking you're following) With me?<</dialog>>
<<dialog "player">>Yeah. Keep going.<</dialog>>
<<dialog "father">>(a small satisfaction) Good.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-1.webp">>
<<image _img "100%">>
<<narrative>>Something you did last time was right. He says so.<</narrative>>
<<dialog "father">>You did that part correctly last week. Without being told.
(direct, not effusive)
You've got good instincts for it.<</dialog>>
<<dialog "player">>(surprised) Really?<</dialog>>
<<dialog "father">>(back to work) Yes. Don't be surprised. I'd say if you hadn't.<</dialog>>`,
friendship: 3, trust: 2, love: 1
}
],
stories: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's working on something and starts talking. Not to the room. To you.<</narrative>>
<<dialog "father">>My first job after university. I was terrible.
(not looking up)
I mean, technically, I didn't know most of what I needed to know.
(a pause)
My supervisor kept me anyway. Said confidence beats knowledge in the first six months.
(beat)
He was right. Eventually.<</dialog>>
<<dialog "player">>You seem like you knew what you were doing from the start.<</dialog>>
<<dialog "father">>(quietly amused) No. I'm good at seeming like I do.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He picks up the old chisels and something surfaces. A memory he actually wants to share.<</narrative>>
<<dialog "father">>My grandfather had a workshop. Smaller than this. Sawdust on everything.
(examining one)
He'd let me sit in there for hours. Didn't talk much. I'd just watch and hand him things.
(a beat)
Best education I ever got. No one talked, no one explained. You just absorbed it.<</dialog>>
<<dialog "player">>Is that how you learn best?<</dialog>>
<<dialog "father">>(thinking) Yeah. Still. Watching first.
(looks at you)
You do the same, actually.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He talks without preamble. A story that just surfaced.<</narrative>>
<<dialog "father">>When your mother and I bought our first house, I didn't know what I was doing with anything. Plumbing, wiring. I was making it up.
(wryly)
I fixed one thing and broke two others. Every time.
(beat)
She never made me feel stupid for it. Just handed me a cup of tea and said 'what's next'.
(quietly)
That's worth a lot.<</dialog>>
<<dialog "player">>She still does that.<</dialog>>
<<dialog "father">>(simple) I know.
(back to work, quiet for a while)<</dialog>>`,
friendship: 3, love: 2
}
],
realTalk: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's not doing anything. Just standing in the garage with a mug of tea. Something's on his mind.<</narrative>>
<<dialog "father">>Moving here. It was the right call. I know it didn't feel like it at first.
(looking at you)
But for this family. For where we're going.
(back to the mug)
It was right.<</dialog>>
<<dialog "player">>I think so too. Now.<</dialog>>
<<dialog "father">>(nodding, quietly) Good.
(a pause)
Thank you for not for giving it a chance.<</dialog>>`,
friendship: 3, trust: 2, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He asks something out of nowhere, like he's been sitting on it.<</narrative>>
<<dialog "father">>Are you happy here? Genuinely. You don't have to say yes.
(direct)<</dialog>>
<<dialog "player">>(thinking honestly) Getting there. More than when we arrived.<</dialog>>
<<dialog "father">>(relieved in a quiet way) That's enough. For now.
(back to work)
Tell me if that changes.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/garageEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He finishes for the night. Wipes down the workbench. Turns off the radio. The wind-down ritual.<</narrative>>
<<dialog "father">>Good session tonight.
(looking at what got done)
You're useful in here.
(meeting your eye)
I mean that as the actual best thing I know how to say.<</dialog>>
<<dialog "player">>I know.<</dialog>>
<<dialog "father">>(a real, quiet smile. Brief.) Right.
(heading inside)
Come on.<</dialog>>`,
friendship: 3, love: 2, trust: 1
}
]
},
fhParentsRoom_postWork_morning: {
leaving: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's almost ready. He catches your reflection in the mirror as you appear in the doorway.<</narrative>>
<<dialog "father">>(without stopping) You're up.<</dialog>>
<<dialog "player">>Yeah. Have a good day.<</dialog>>
<<dialog "father">>(pausing, something in the directness of it) Thanks.
(turning briefly)
You too.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's grabbing his jacket when he spots something on the desk. The note you left.<</narrative>>
<<dialog "father">>(reading it) What's this?<</dialog>>
<<dialog "player">>The plumber's number. You mentioned the downstairs bathroom last week.<</dialog>>
<<dialog "father">>(a beat, genuinely caught off guard by the thoughtfulness) I did.
(pocketing it)
Thank you. I'll call after nine.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's halfway out when he doubles back. Remembered something.<</narrative>>
<<dialog "father">>I've been thinking about what you said last night.
(brief, not making it a moment)
You were right. I'll deal with it differently.<</dialog>>
<<dialog "player">>I wasn't sure if I overstepped.<</dialog>>
<<dialog "father">>(firmly) You didn't.
(going)
See you tonight.<</dialog>>`,
friendship: 3, trust: 2, love: 1
}
],
preparing: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's checking his bag. The new morning efficiency. He looks up when you appear.<</narrative>>
<<dialog "father">>I've started leaving earlier. Twenty minutes changes the whole drive.
(zipping his bag)
Fifteen minutes of traffic, not forty-five.
(to you)
Small things.<</dialog>>
<<dialog "player">>You seem less wound up in the mornings.<</dialog>>
<<dialog "father">>(a beat, receiving that) Yeah. I think I am.
(heading to the door)
Good to know it shows.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He pauses at the dresser, looking at a photo there. The family one. A brief moment.<</narrative>>
<<dialog "player">>(quietly) Good photo.<</dialog>>
<<dialog "father">>(not turning) Yeah.
(picking up his watch)
I put it there so I see it before I go in the morning.
(simply)
Helps.<</dialog>>
<<dialog "player">>Helps with what?<</dialog>>
<<dialog "father">>(heading out, matter-of-fact) Perspective.<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's tying his tie and catches you watching in the mirror.<</narrative>>
<<dialog "father">>(drily) Do you want to do it?<</dialog>>
<<dialog "player">>I don't know how.<</dialog>>
<<dialog "father">>(continuing) Four-in-hand. It's the only useful one. I'll show you sometime.
(finishing the knot)
Actually, come here.<</dialog>>
<<narrative>>He undoes it and retties it slowly, talking you through it.<</narrative>>
<<dialog "father">>There. Now you know.<</dialog>>`,
friendship: 3, trust: 1, love: 1
}
],
moment: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's almost at the door when he turns back. Not in a hurry for once.<</narrative>>
<<dialog "father">>Can I ask you something?
(not waiting long)
Are you alright? Not fine. Actually alright.<</dialog>>
<<dialog "player">>(thinking) Yeah. I am.<</dialog>>
<<dialog "father">>(searching your face briefly) Okay. Good.
(going)
See you tonight.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He passes you in the doorway and pauses, close, briefly.<</narrative>>
<<dialog "father">>You've been good to your mother this week.
(not looking at you, but meaning it fully)
I see it.<</dialog>>
<<dialog "player">>She's easy to be good to.<</dialog>>
<<dialog "father">>(something in that, quiet) Yeah. She is.
(heading downstairs)<</dialog>>`,
friendship: 3, love: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's turned to go and stops. Looks back over his shoulder. Direct, real.<</narrative>>
<<dialog "father">>I'm glad you're here.
(simply, not making it a speech)
That's all.<</dialog>>
<<dialog "player">>(quietly) Me too.<</dialog>>
<<dialog "father">>(nods once. Goes.)<</dialog>>`,
friendship: 3, love: 2, trust: 2
}
]
},
fhParentsRoom_postWork_evening: {
vulnerable: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's sitting in the dim room. Jacket off, door half-open. Not hiding. Just decompressing in the quiet.<</narrative>>
<<dialog "father">>(low) Come in if you want.<</dialog>>
<<dialog "player">>(sitting in the chair) Rough day?<</dialog>>
<<dialog "father">>(long pause) Not rough. Just a lot.
(looking at the ceiling)
Some days the decisions feel heavier than others.<</dialog>>
<<dialog "player">>You always look like you know what to do.<</dialog>>
<<dialog "father">>(quietly) I know what I'd tell someone else to do.
(back to the ceiling)
Doesn't mean I find it easy.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's putting out tomorrow's clothes. Methodical. A Sunday habit on a weeknight. He talks without turning.<</narrative>>
<<dialog "father">>You know what I thought about today, out of nowhere?
(not waiting)
When you were maybe eleven. You fixed the chain on your bike yourself. Didn't ask, just did it.<</dialog>>
<<dialog "player">>I remember that.<</dialog>>
<<dialog "father">>(still not turning) I wasn't sure whether to say something or just let you get on with it.
(a beat)
I let you.
(quietly)
That was the right call.<</dialog>>`,
friendship: 3, love: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's at the end of the day. Genuinely tired. Genuinely himself.<</narrative>>
<<dialog "father">>(sitting on the bed) I forget sometimes that you're not a child anymore. I know that's obvious to say.
(looking at you properly)
You carry yourself differently than you used to here.
(simply)
It's good. You're good.<</dialog>>
<<dialog "player">>(quietly) Thanks, Dad.<</dialog>>
<<dialog "father">>(nodding, once. That word did something to him.)
Goodnight.<</dialog>>`,
friendship: 3, love: 2, trust: 2
}
],
nightRoutine: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's reading. Actually reading now, not just holding the book. He pauses when you knock.<</narrative>>
<<dialog "father">>(setting the page) You alright?<</dialog>>
<<dialog "player">>Yeah. Just goodnight.<</dialog>>
<<dialog "father">>(studying you for a moment) Sit down a minute.
(making room on the edge of the bed)
Tell me something good that happened today. One thing.<</dialog>>
<<dialog "player">>(thinking, then answering)<</dialog>>
<<dialog "father">>(listening, nodding) Good.
(back to the book)
Goodnight.<</dialog>>`,
friendship: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He looks up from whatever he's reading and catches you in the doorway.<</narrative>>
<<dialog "father">>(simply) You know I don't always say the right thing.
(back to the book)
But I mean well. With all of it. I want you to know that.<</dialog>>
<<dialog "player">>I know.<</dialog>>
<<dialog "father">>(a beat, he believed that) Good.
(turning off the lamp)
Goodnight.<</dialog>>`,
friendship: 3, love: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's almost asleep but not quite. That half-state where things come out more easily.<</narrative>>
<<dialog "father">>(low, half to himself) You know what I keep thinking?
(a pause)
That I'd like to do this better. Be more present.
(opening his eyes briefly)
I'm working on it. In case you hadn't noticed.<</dialog>>
<<dialog "player">>I've noticed.<</dialog>>
<<dialog "father">>(closing his eyes again, something settled) Good.
(quietly)
Goodnight.<</dialog>>`,
friendship: 3, love: 2, trust: 2
}
],
closing: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's at the desk. Not working. Just sitting with a cup of tea in the quiet.<</narrative>>
<<dialog "father">>(looking up) Come in.
(gesturing to the chair)
I was just thinking.<</dialog>>
<<dialog "player">>(sitting) About what?<</dialog>>
<<dialog "father">>About how I want the next few years to go. For all of us.
(simply)
I think it's going to be good.
(sipping his tea)
I just wanted to say that out loud to someone.<</dialog>>`,
friendship: 3, love: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's putting the room in order before bed. There's a quiet, settled energy about him tonight.<</narrative>>
<<dialog "player">>You seem I don't know. Good tonight.<</dialog>>
<<dialog "father">>(pausing) I had a good day. And I came home to a good house.
(back to what he's doing)
That's enough. More than enough.<</dialog>>
<<dialog "player">>(quietly) Yeah.<</dialog>>
<<dialog "father">>(a brief, real look at you) Goodnight.<</dialog>>`,
friendship: 3, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/postwork/level2/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He sits on the edge of the bed and looks at you in the doorway. Fully present. Guard completely gone.<</narrative>>
<<dialog "father">>I know I'm not always easy.
(direct)
I know that. And I know it matters.
(simply)
I want you to know that I know.<</dialog>>
<<dialog "player">>(after a moment) It's okay, Dad. Really.<</dialog>>
<<dialog "father">>(holds your gaze a second, something completed there)
Good.
(turning off the lamp)
Goodnight.<</dialog>>`,
friendship: 3, love: 3, trust: 2
}
]
}
}>>/* ==========================================
FATHER TALK TOPICS PRE-WORK, LEVEL 1
IMG MAPPING:
kitchenMorning: coffee=1, news=2, schedule=3
kitchenEvening: dinner=1, family=2, plans=3
livingroomMorning: news=1, reading=2, settling=3
livingroomAfternoon: unpacking=1, sports=2, smalltalk=3
livingroomEvening: tv=1, decompressing=2, family=3
backyardAfternoon: yardwork=1, tools=2, observation=3
garageMorning: project=1, tools=2, plans=3
garageAfternoon: carwork=1, radio=2, break=3
parentsRoomMorning: reading=1, gettingReady=2, morning=3
parentsRoomEvening: windingDown=1, quiet=2, tomorrow=3
========================================== */
<<set setup.fatherTopics = setup.fatherTopics || {}>>
<<set setup.fatherTopics.preWork = setup.fatherTopics.preWork || {}>>
<<set setup.fatherTopics.preWork.level1 = {
fhKitchen_preWork_morning: {
coffee: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's already set the coffee maker going. He nods when you walk in. No big greeting.<</narrative>>
<<dialog "father">>Morning. Coffee's almost done.<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "father">>(sitting down with the tablet) Sleep okay?<</dialog>>
<<dialog "player">>More or less.<</dialog>>
<<dialog "father">>(glancing over) House makes noise. Takes some getting used to.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's standing by the counter waiting for the kettle, scrolling his phone idly.<</narrative>>
<<dialog "father">>They really sold us on this coffee maker. Your mother's idea. Good one, actually.<</dialog>>
<<dialog "player">>It's not bad.<</dialog>>
<<dialog "father">>(noncommitally) Better than what we had.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He pours a second mug before you ask and slides it across the counter toward you.<</narrative>>
<<dialog "player">>Oh thanks.<</dialog>>
<<dialog "father">>It brewed too much anyway.<</dialog>>
<<dialog "player">>(sitting down) What are your plans today?<</dialog>>
<<dialog "father">>(considering) Garage. Then probably nothing.<</dialog>>`,
friendship: 2
}
],
news: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's reading something on his tablet, vaguely engrossed. He holds it up briefly when he sees you.<</narrative>>
<<dialog "father">>There's a thing about property taxes in this area. Might affect us.
(putting it down)
I'll read the whole thing later.<</dialog>>
<<dialog "player">>Is it bad?<</dialog>>
<<dialog "father">>(shrugging) Unclear. Probably fine.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's muttering at something on his phone. Looks up when you sit.<</narrative>>
<<dialog "father">>Traffic is already a mess at 7am on that main road. Good thing I'm working from home half the week.<</dialog>>
<<dialog "player">>Where's the office again?<</dialog>>
<<dialog "father">>Twenty-minute drive. Not terrible. Depends on the lights.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He sets his phone face down. Done with the news for now.<</narrative>>
<<dialog "father">>Waste of time, half of it. Same things, different words.
(standing)
You want toast?<</dialog>>
<<dialog "player">>Sure.<</dialog>>
<<dialog "father">>(already at the toaster) Good call.<</dialog>>`,
friendship: 2
}
],
schedule: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's got a paper notepad out, which feels like an old habit in a house full of screens.<</narrative>>
<<dialog "father">>I'm making a list of things to fix before I start work.
(tapping the pad)
Otherwise it just won't happen.<</dialog>>
<<dialog "player">>Like what?<</dialog>>
<<dialog "father">>Shelf in the hallway. That tap in the downstairs bathroom. A few things in the garage.<</dialog>>
<<dialog "player">>I can help with some of those.<</dialog>>
<<dialog "father">>(looking up, briefly pleased) Yeah. Maybe.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He pours his coffee and addresses you without much preamble.<</narrative>>
<<dialog "father">>You doing anything this morning?<</dialog>>
<<dialog "player">>Nothing fixed. Why?<</dialog>>
<<dialog "father">>I was going to drive around, get the layout of the area. Map in my head. You're welcome to come if you want.<</dialog>>
<<dialog "player">>Yeah. I'll come.<</dialog>>
<<dialog "father">>(nodding, looking back at his coffee) Good. Twenty minutes.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He checks his watch. An actual watch, not his phone.<</narrative>>
<<dialog "father">>Starting work in about a week. Feels strange, saying that.
(almost to himself)
Good strange. I think.<</dialog>>
<<dialog "player">>Nervous?<</dialog>>
<<dialog "father">>(quickly) No.
(pause)
Maybe a bit.<</dialog>>`,
friendship: 1, trust: 1
}
]
},
fhKitchen_preWork_evening: {
dinner: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's clearing dishes, working through it methodically.<</narrative>>
<<dialog "father">>Dinner was good.<</dialog>>
<<dialog "player">>Yeah, it was.<</dialog>>
<<dialog "father">>(rinsing a plate) Your mother's been cooking a lot since we moved. She does that when she's settling in.<</dialog>>
<<dialog "player">>How do you know?<</dialog>>
<<dialog "father">>(dryly) Twenty years of observation.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's at the sink. He nods at the pile of dishes beside you.<</narrative>>
<<dialog "father">>You can dry those if you want.<</dialog>>
<<dialog "player">>(picking up a towel) Sure.<</dialog>>
<<narrative>>You work in silence for a bit. It's not uncomfortable.<</narrative>>
<<dialog "father">>(after a while) This kitchen's bigger than the last one.<</dialog>>
<<dialog "player">>Yeah, it is.<</dialog>>
<<dialog "father">>Good. I always wanted a bigger kitchen.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He pours himself a small glass of water after clearing the table. Winding down.<</narrative>>
<<dialog "father">>Good day?<</dialog>>
<<dialog "player">>Not bad. You?<</dialog>>
<<dialog "father">>(thinking) Productive. I got the garage halfway sorted.
(pause)
That's something.<</dialog>>`,
friendship: 1
}
],
family: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He leans on the counter, arms crossed, relaxed end-of-day posture.<</narrative>>
<<dialog "father">>Jake seemed better today.<</dialog>>
<<dialog "player">>Yeah?<</dialog>>
<<dialog "father">>He was outside after lunch. That's new.
(simply)
Progress.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He brings something up obliquely. Reading the room.<</narrative>>
<<dialog "father">>Your mother seems happy with the place. That matters.
(glancing at you)
You settling in okay?<</dialog>>
<<dialog "player">>Getting there.<</dialog>>
<<dialog "father">>(nodding) Give it a few more weeks. It adds up.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He refills the water filter jug. Small thing. He does it without thinking.<</narrative>>
<<dialog "father">>When I start work, the evenings'll be different. Shorter.
(matter-of-fact)
I'll be tired. Fair warning.<</dialog>>
<<dialog "player">>I know.<</dialog>>
<<dialog "father">>(simply) Good. Just saying.<</dialog>>`,
friendship: 1, trust: 1
}
],
plans: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's got a notepad again. This one looks like a shopping list crossed with a project plan.<</narrative>>
<<dialog "father">>I want to put a shelf unit along that back garage wall before I start work. Something proper. You know how to use a drill?<</dialog>>
<<dialog "player">>More or less.<</dialog>>
<<dialog "father">>(levelly) That's good enough. I'll show you the rest.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He looks at the kitchen calendar on the wall. An actual paper one, religiously kept.<</narrative>>
<<dialog "father">>Start date's eight days. I've been doing maths on it all week.
(turning)
Does that seem strange?<</dialog>>
<<dialog "player">>Not really.<</dialog>>
<<dialog "father">>(returning to the calendar) Good. I think it's fine. Just different.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's already thinking about tomorrow as he turns off the kitchen light.<</narrative>>
<<dialog "father">>If the weather holds I'll get the backyard sorted in the morning. Needs work.<</dialog>>
<<dialog "player">>Want help?<</dialog>>
<<dialog "father">>(briefly surprised) If you're up for it.
(a beat)
Yeah. Sure.<</dialog>>`,
friendship: 2
}
]
},
fhLivingroom_preWork_morning: {
news: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's on the couch with the morning news on, half-watching. He glances up.<</narrative>>
<<dialog "father">>There's that story again. Third time this week.<</dialog>>
<<dialog "player">>Which one?<</dialog>>
<<dialog "father">>(pointing at the screen) That development project in the city. Planning battle. Very boring, very important apparently.<</dialog>>
<<dialog "player">>And you're watching it anyway?<</dialog>>
<<dialog "father">>(flat) It's the morning. Standards are lower.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He turns the TV down when you sit. Slight but noticeable.<</narrative>>
<<dialog "father">>Market's doing something odd. My old colleague messaged about it.
(vaguely)
I'm not worried.<</dialog>>
<<dialog "player">>Should you be?<</dialog>>
<<dialog "father">>No. We're fine. Just an interesting observation.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He mutes the TV completely and sets his mug down.<</narrative>>
<<dialog "father">>I should probably stop watching these in the morning. Bad habit.
(not doing anything about it)
Informative though.<</dialog>>
<<dialog "player">>(sitting) I won't tell anyone.<</dialog>>
<<dialog "father">>(slight smile) Appreciated.<</dialog>>`,
friendship: 2
}
],
reading: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's got a paperback with a worn spine. Clearly re-read. He looks up without putting it down.<</narrative>>
<<dialog "player">>What are you reading?<</dialog>>
<<dialog "father">>(showing the cover) Engineering history. Bridges.<</dialog>>
<<dialog "player">>For fun?<</dialog>>
<<dialog "father">>You'd be surprised.
(returning to it)
Sit down. You don't have to talk.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's finished a chapter and set the book on the side table. Looking at nothing in particular.<</narrative>>
<<dialog "father">>I forget what it's like to have unscheduled mornings. Haven't had them in twenty years.<</dialog>>
<<dialog "player">>How is it?<</dialog>>
<<dialog "father">>(honestly) Strange. Slightly uncomfortable.
(a pause)
Getting better.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's in the reading chair. He's already claimed it as his spot.<</narrative>>
<<dialog "father">>You want anything? I was going to get more coffee.<</dialog>>
<<dialog "player">>Sure, if you're going.<</dialog>>
<<dialog "father">>(already standing) Right.<</dialog>>
<<narrative>>He comes back with two mugs. Sets yours down. Goes back to his book.<</narrative>>`,
friendship: 2
}
],
settling: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He looks at the living room with the expression of someone drawing up a mental map.<</narrative>>
<<dialog "father">>I still haven't decided where the router goes. It matters more than people think.
(looking at you)
Do you stream much?<</dialog>>
<<dialog "player">>Sometimes. Why?<</dialog>>
<<dialog "father">>I'm working out the strongest signal placement.
(briskly)
I'll test it this afternoon.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He narrows his eyes at a slightly crooked picture on the wall. Says nothing. Gets up. Straightens it. Sits back down.<</narrative>>
<<dialog "player">>Better?<</dialog>>
<<dialog "father">>(back to his coffee) Much.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He looks around the room. You can tell he's building a mental inventory.<</narrative>>
<<dialog "father">>I want to get a proper lamp in here. The overhead light is wrong.<</dialog>>
<<dialog "player">>Wrong how?<</dialog>>
<<dialog "father">>(matter-of-factly) Wrong colour temperature. Makes everything look clinical.
(standing)
I'll look online later.<</dialog>>`,
friendship: 1
}
]
},
fhLivingroom_preWork_afternoon: {
unpacking: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's sorting through a box. Not urgently. Just steadily.<</narrative>>
<<dialog "father">>I found that cable box I've been looking for.
(holding it up)
Knew I packed it in here somewhere.<</dialog>>
<<dialog "player">>What's it for?<</dialog>>
<<dialog "father">>The old gaming setup. I thought about setting it up downstairs.
(putting it aside)
Maybe.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's trying to hang something on the wall and it's not going smoothly. He steps back.<</narrative>>
<<dialog "father">>Walls in this place are plaster over something odd. The drill doesn't like it.<</dialog>>
<<dialog "player">>Do you need help?<</dialog>>
<<dialog "father">>(looking at the wall, resigned) No. I'll figure it out.
(pause)
...maybe.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's sitting with a box in his lap, but not doing much. Just looking at the room.<</narrative>>
<<dialog "father">>Different light in the afternoon. Didn't notice it when we viewed the place.
(glancing at you)
Good different. Nice.<</dialog>>
<<dialog "player">>Yeah, I noticed that too.<</dialog>>
<<dialog "father">>(going back to the box) Good.<</dialog>>`,
friendship: 2
}
],
sports: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's got a game on in the background he's pretending not to watch while doing other things.<</narrative>>
<<dialog "father">>That penalty was nonsense.<</dialog>>
<<dialog "player">>I didn't see it.<</dialog>>
<<dialog "father">>(rewinding instantly) Look.
(pointing)
There. His feet were in. Clear call.<</dialog>>
<<dialog "player">>Hard to tell.<</dialog>>
<<dialog "father">>(dismissively) It's not that hard to tell.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He looks up from his tablet. Sports summary, clearly.<</narrative>>
<<dialog "father">>Do you follow any sport?<</dialog>>
<<dialog "player">>A bit. Depends on the season.<</dialog>>
<<dialog "father">>(mildly surprised) What do you watch?<</dialog>>
<<dialog "player">>(answering)<</dialog>>
<<dialog "father">>(considering) Hm.
(back to his tablet)
Fair enough.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's watching highlights with the sound low. Lazy-afternoon mode.<</narrative>>
<<dialog "father">>This is my version of switching off. Don't need to think about it. It just happens.<</dialog>>
<<dialog "player">>That's the appeal?<</dialog>>
<<dialog "father">>That's the whole appeal.
(glancing at you)
You can watch if you want. I won't explain every play.<</dialog>>`,
friendship: 2
}
],
smalltalk: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's just sitting. You're just sitting. It's fine.<</narrative>>
<<dialog "father">>Quiet day.<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "father">>(after a moment) Good.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He taps the arm of his chair absently. Just thinking.<</narrative>>
<<dialog "father">>I should look up where the nearest hardware store is.
(not moving)
I'll do it later.<</dialog>>
<<dialog "player">>I passed one on the main road, I think. Left at the lights?<</dialog>>
<<dialog "father">>(sitting up slightly) Yeah? Good to know.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He looks over at you for a moment. The look of a parent doing a quiet background check.<</narrative>>
<<dialog "father">>You alright?<</dialog>>
<<dialog "player">>Yeah, I'm fine.<</dialog>>
<<dialog "father">>(satisfied) Good.
(back to whatever he was doing)
Ask if you need anything.<</dialog>>`,
friendship: 2, trust: 1
}
]
},
fhLivingroom_preWork_evening: {
tv: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's watching something. A documentary, looks like. He shifts when you sit down, making room without comment.<</narrative>>
<<dialog "father">>This one's actually good. About the construction of the Channel Tunnel.<</dialog>>
<<dialog "player">>Is it?<</dialog>>
<<dialog "father">>The engineering is interesting. Don't need to care about the politics.
(glancing at you)
You don't have to watch if it's not your thing.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's half-asleep in the armchair with the game still on. He comes to when you walk in.<</narrative>>
<<dialog "father">>(sitting up) What time is it?<</dialog>>
<<dialog "player">>About nine.<</dialog>>
<<dialog "father">>(rubbing his eyes) I wasn't asleep.
(clearly was)
Resting my eyes.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He looks up from the TV and nods at the couch.<</narrative>>
<<dialog "player">>(sitting) What's on?<</dialog>>
<<dialog "father">>Sports recap. Then probably nothing worth watching.<</dialog>>
<<dialog "player">>We could put something else on.<</dialog>>
<<dialog "father">>(considering) What did you have in mind?<</dialog>>`,
friendship: 2
}
],
decompressing: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's just sitting. Not doing anything. Not watching anything. Unusual for him.<</narrative>>
<<dialog "father">>Good day?<</dialog>>
<<dialog "player">>Yeah. You?<</dialog>>
<<dialog "father">>Busy enough. In a satisfying way.
(pause)
That's the goal.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's got a glass of water and the look of someone doing nothing on purpose.<</narrative>>
<<dialog "father">>I've decided to try and actually relax this week. Before everything starts.
(flatly)
It's harder than expected.<</dialog>>
<<dialog "player">>You're doing okay from here.<</dialog>>
<<dialog "father">>(mildly) I'm trying.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He takes a long breath and sets his phone on the arm of the chair, screen-down.<</narrative>>
<<dialog "father">>I'm going to read for a bit. Real book, no screens.
(looking at you)
What do you do to switch off?<</dialog>>
<<dialog "player">>(answering)<</dialog>>
<<dialog "father">>(listening, nodding) Hm. Not bad.<</dialog>>`,
friendship: 2, trust: 1
}
],
family: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He leans forward with his elbows on his knees, looking at the floor for a moment.<</narrative>>
<<dialog "father">>I want this place to work. For everyone.
(simply)
Just so you know that.<</dialog>>
<<dialog "player">>I know.<</dialog>>
<<dialog "father">>(nodding, sitting back) Good.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He mentions Jake with the careful tone of someone monitoring a situation.<</narrative>>
<<dialog "father">>Jake ate dinner with us again tonight. Didn't disappear as soon as he was done.<</dialog>>
<<dialog "player">>That's good.<</dialog>>
<<dialog "father">>Yeah.
(beat)
Small things.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's looking at a family photo he's placed on the new shelf. Clearly just unpacked.<</narrative>>
<<dialog "father">>I found this in the last box. Thought I'd lost it.
(glancing at you briefly)
Good photo.<</dialog>>
<<dialog "player">>(looking) Yeah. It is.<</dialog>>
<<dialog "father">>(back to the TV) Good.<</dialog>>`,
friendship: 2
}
]
},
fhBackyard_preWork_afternoon: {
yardwork: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's doing a slow perimeter walk. Assessing, not relaxing.<</narrative>>
<<dialog "father">>That fence panel on the left is loose. I'll get to it.
(pausing)
Back garden's bigger than it looked in the listing. That's good.<</dialog>>
<<dialog "player">>What are you planning out here?<</dialog>>
<<dialog "father">>(briefly energised) Not sure yet. Depends on what the ground's like.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's prodding the lawn with his foot. Testing the ground.<</narrative>>
<<dialog "father">>Soft in that corner. Drainage issue, maybe.
(to himself)
I'll dig down and check.<</dialog>>
<<dialog "player">>Need a hand?<</dialog>>
<<dialog "father">>(looking up) You know anything about drainage?<</dialog>>
<<dialog "player">>Not really.<</dialog>>
<<dialog "father">>(accepting this) Bring the spade then. Two pairs of hands still helps.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's standing in the middle of the yard with his arms crossed, looking satisfied by something.<</narrative>>
<<dialog "father">>Cleared that overgrowth along the back wall. Took three hours.
(without complaint)
Needed doing.<</dialog>>
<<dialog "player">>Looks better.<</dialog>>
<<dialog "father">>(matter-of-factly) Looked like nobody cared. Now it doesn't.<</dialog>>`,
friendship: 2
}
],
tools: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's got a collection of garden tools out and is sorting through them methodically.<</narrative>>
<<dialog "father">>Half of these are useless. I've kept them for years and never used them once.
(setting one aside)
This one stays. Everything else we'll see.<</dialog>>
<<dialog "player">>Which one?<</dialog>>
<<dialog "father">>(holding it up) Good spade. Balance is right. You can always tell a good spade by the balance.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He hands you something without much preamble.<</narrative>>
<<dialog "father">>Hold that level. Take that end.
(positioning it)
Tell me when the bubble's centered.<</dialog>>
<<dialog "player">>Okay there.<</dialog>>
<<dialog "father">>(marking the post) Good.
(moving on without ceremony)
Next one.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's oiling something. Methodical, unhurried.<</narrative>>
<<dialog "father">>Tools need looking after or they rust. Simple as that.
(not really speaking to you, more to himself)
Ten minutes now saves an hour later.<</dialog>>
<<dialog "player">>(sitting on the step nearby) Makes sense.<</dialog>>
<<dialog "father">>(glancing up) You can do yours too, when I'm done.<</dialog>>`,
friendship: 1
}
],
observation: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's standing with a mug of tea, just looking at the garden. No urgency to it.<</narrative>>
<<dialog "father">>Good afternoon for it. Not too hot.<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "father">>(quiet for a moment) This is good, actually. Having a garden again. Last place had nothing.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He sits on the back step beside you. Unusual. He's not normally the sitting type.<</narrative>>
<<dialog "father">>Your mother's got plans for that border. I've been given a list.
(dryly)
A very specific list.<</dialog>>
<<dialog "player">>(smiling) How detailed?<</dialog>>
<<dialog "father">>It has a colour-coded chart.
(almost impressed)
I'm going to follow it precisely.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's watching a bird land on the fence. He doesn't point it out. He just watches.<</narrative>>
<<dialog "player">>What kind is it?<</dialog>>
<<dialog "father">>(without looking away) Blue tit, I think. Hard to tell from here.
(it flies away)
Gone.<</dialog>>
<<dialog "player">>You like birds?<</dialog>>
<<dialog "father">>(walking away, matter-of-fact) I notice them. Not the same thing.<</dialog>>`,
friendship: 2
}
]
},
fhGarage_preWork_morning: {
project: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's surveying the garage like a general with a battlefield to organise.<</narrative>>
<<dialog "father">>Right. I want the workbench along that wall. Tools hanging above.
(pulling out a tape measure)
And the shelf unit along the back.<</dialog>>
<<dialog "player">>You've thought this through.<</dialog>>
<<dialog "father">>(measuring) All week. Help me with this end.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's got the car up on ramps already. Rare morning motivation.<</narrative>>
<<dialog "father">>I want to get the oil change done before work starts. Easier now than when I've got somewhere to be.
(looking under the car)
Hand me that socket wrench. The one on the left.<</dialog>>
<<dialog "player">>(handing it) This one?<</dialog>>
<<dialog "father">>That one.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's labelling storage boxes with a label maker. Intensely.<</narrative>>
<<dialog "player">>Did you bring a label maker?<</dialog>>
<<dialog "father">>(not looking up) I have had this label maker for eleven years. It has never let me down.
(applying a label)
Clear labelling is the foundation of any organised system.<</dialog>>
<<dialog "player">>Sure.<</dialog>>
<<dialog "father">>(handing you one) You can do that stack.<</dialog>>`,
friendship: 1
}
],
tools: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's hanging tools on a pegboard, placing each one exactly.<</narrative>>
<<dialog "father">>Everything has a place. If you don't know where it is, you can't find it when you need it.
(hanging a wrench)
Sounds obvious. Most people still don't do it.<</dialog>>
<<dialog "player">>Can I help?<</dialog>>
<<dialog "father">>(gesturing to a box) Sort those by size. Biggest on the left.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He finds something in the toolbox and holds it up with quiet satisfaction.<</narrative>>
<<dialog "father">>My father gave me this. I thought I'd lost it in the move.
(looking at it)
Good set of chisels.<</dialog>>
<<dialog "player">>You use them much?<</dialog>>
<<dialog "father">>Not often. But when you need a good chisel, nothing else works.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He hands you a drill and stands back, arms crossed, expectant.<</narrative>>
<<dialog "father">>Let me see how you use it.<</dialog>>
<<dialog "player">>Right now?<</dialog>>
<<dialog "father">>(indicating a piece of scrap wood) That piece. Pilot hole. Middle.<</dialog>>
<<dialog "player">>(doing it)<</dialog>>
<<dialog "father">>(watching) Good. Firm grip, let the drill do the work. You've done it before.<</dialog>>`,
friendship: 2, trust: 1
}
],
plans: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's got a rough sketch on a notepad. The garage layout in rectangles and arrows.<</narrative>>
<<dialog "father">>If I move the chest freezer here, that gives me enough room for a proper workbench.
(looking up)
What do you think?<</dialog>>
<<dialog "player">>Seems logical.<</dialog>>
<<dialog "father">>(nodding) It is logical. I just like a second opinion.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's checking the garage door mechanism with focused attention.<</narrative>>
<<dialog "father">>I want to wire this to the house system before I start work. Automated opener.
(to himself)
Shouldn't be complicated.<</dialog>>
<<dialog "player">>Do you know how to do that?<</dialog>>
<<dialog "father">>(briefly) I'll figure it out. I've done worse.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He looks around the garage with a kind of quiet ownership. This is his space.<</narrative>>
<<dialog "father">>When I have this right I'll put the car in here and actually work on it properly. Like I used to.
(slight energy behind that)
I haven't had space for it. Years.<</dialog>>
<<dialog "player">>What do you do to the car?<</dialog>>
<<dialog "father">>Whatever needs doing. That's the point.<</dialog>>`,
friendship: 2
}
]
},
fhGarage_preWork_afternoon: {
carwork: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He's under the bonnet, absorbed in something. He looks up with engine oil on his hands.<</narrative>>
<<dialog "father">>Come look at this.
(pointing)
You see that? That's the alternator. It's been making a noise.
(to himself)
Not serious. Probably the belt.<</dialog>>
<<dialog "player">>What do you do about it?<</dialog>>
<<dialog "father">>Replace the belt. I'll show you if you want.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He hands you a cloth without looking up from the engine.<</narrative>>
<<dialog "father">>Wipe down that part there. The sensor.<</dialog>>
<<dialog "player">>(doing it) Like this?<</dialog>>
<<dialog "father">>(glancing) Yeah. Gentle. You don't need force, just coverage.
(going back to his work)
Good.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>He steps back from the car and stretches his back. There's a satisfying crack.<</narrative>>
<<dialog "father">>That's the brakes sorted.
(genuine satisfaction)
Was binding on the left. Won't do that anymore.<</dialog>>
<<dialog "player">>You fixed it yourself?<</dialog>>
<<dialog "father">>It's not complicated if you know what you're looking at.
(wiping his hands)
I'll teach you sometime. It's useful.<</dialog>>`,
friendship: 2, trust: 1
}
],
radio: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He's got a small radio going in the corner. Talk radio, mid-commentary on something.<</narrative>>
<<dialog "father">>(half-listening) They're wrong about that. The whole premise is wrong.
(noticing you)
You follow this?<</dialog>>
<<dialog "player">>Not really. What is it?<</dialog>>
<<dialog "father">>(going back to work) Doesn't matter. They're still wrong.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>The radio switches to music. Something old. Something he clearly recognizes.<</narrative>>
<<dialog "father">>(not looking up) This was your mother's favourite. When we were first together.<</dialog>>
<<dialog "player">>Does she still like it?<</dialog>>
<<dialog "father">>She would if she heard it.
(matter-of-factly)
She's forgotten about it, I think.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>He turns the radio off when it starts playing something he disapproves of.<</narrative>>
<<dialog "father">>(mildly) They ruined the second half of this show. Used to be good.
(working in silence now)
Quiet's fine too.<</dialog>>
<<dialog "player">>(sitting nearby) Yeah.<</dialog>>
<<dialog "father">>(after a while) You can put something else on if you want. You probably have something.<</dialog>>`,
friendship: 2
}
],
break: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He leans against the workbench with a thermos of tea. A planned break, not a pause.<</narrative>>
<<dialog "father">>Good progress today.
(not elaborating)
Sit down if you want.<</dialog>>
<<dialog "player">>(sitting on a stool) What's left to do?<</dialog>>
<<dialog "father">>A fair amount. But I'm not doing all of it today.
(quietly)
That's new, for me.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He hands you a thermos cup without asking.<</narrative>>
<<dialog "father">>Tea. It's from this morning so it's not hot.
(shrugging)
Still tea.<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "father">>(leaning back) You doing anything this evening?<</dialog>>
<<dialog "player">>Not much.<</dialog>>
<<dialog "father">>(nodding) Good. Help me lift that shelf unit after dinner then.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/garageAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>He's looking at the car with the expression of someone choosing what not to do.<</narrative>>
<<dialog "father">>I could keep going. But there's such a thing as overdoing it.
(to himself more than you)
That's what your mother says, anyway.<</dialog>>
<<dialog "player">>She's not wrong.<</dialog>>
<<dialog "father">>(brief pause) No. Usually isn't.<</dialog>>`,
friendship: 1
}
]
},
fhParentsRoom_preWork_morning: {
reading: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's sitting on the edge of the bed with a book open. Not really reading it. Just starting the day slowly.<</narrative>>
<<dialog "father">>Morning. You're up early.<</dialog>>
<<dialog "player">>Couldn't sleep. Just checking in.<</dialog>>
<<dialog "father">>(setting the book aside) That's fine. Come in if you want.
(gesturing to the chair in the corner)
I was about to make coffee.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's getting dressed. Tie half-done, still thinking about the day's list.<</narrative>>
<<dialog "player">>Need anything?<</dialog>>
<<dialog "father">>(without turning) No. I'm no.
(then, quieter)
Actually do you know where the other set of keys went? Not the main ones. The spares.<</dialog>>
<<dialog "player">>Kitchen drawer. Left side.<</dialog>>
<<dialog "father">>(pausing) Right. Yes. Thank you.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>He's at the window, looking out at the garden, mug in hand. Pre-day stillness.<</narrative>>
<<dialog "father">>(half to himself) I want to get that fence panel sorted this week.
(noticing you)
Sorry. Thinking out loud.<</dialog>>
<<dialog "player">>What fence panel?<</dialog>>
<<dialog "father">>Left side. It's loose. I'll get to it before the rain comes.
(going back to his mug)
Good morning, by the way.<</dialog>>`,
friendship: 2
}
],
gettingReady: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's checking something on his list. The physical notepad that travels everywhere.<</narrative>>
<<dialog "father">>Eight things today.
(not complaining)
Good number. Achievable.<</dialog>>
<<dialog "player">>What's on it?<</dialog>>
<<dialog "father">>(showing it briefly) Garage mostly. And two calls I've been putting off.
(tucking it away)
You know how those go.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He finds a coin from the old country in his jacket pocket. Holds it a second.<</narrative>>
<<dialog "father">>Always end up with one of these.
(to himself)
I should leave a dish by the door. Like my mother had.<</dialog>>
<<dialog "player">>For coins?<</dialog>>
<<dialog "father">>(slightly embarrassed to have said it) If you put them somewhere they don't vanish.
(pocketing it again)
Forget I said that.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>He's looking for something on the dresser, moving things with patient methodical hands.<</narrative>>
<<dialog "father">>Your mother's getting on well with the neighbours. I heard her talking to the one on the left yesterday.
(finding what he needed)
That's good. She settles through people. Always has.<</dialog>>
<<dialog "player">>What about you?<</dialog>>
<<dialog "father">>(briefly) I settle through the house.
(picking up his wallet, done)
Same result, different method.<</dialog>>`,
friendship: 2, trust: 1
}
],
morning: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's standing at the window with his mug in both hands, looking out at the garden in the early light. He doesn't turn when you appear in the doorway.<</narrative>>
<<dialog "father">>(after a moment) Come in. I'm just looking.
(tilting his head at the garden)
I'm going to plant something along that back wall. Don't know what yet.<</dialog>>
<<dialog "player">>You're getting into gardening?<</dialog>>
<<dialog "father">>(measured) I'm getting into having a garden. It's different.
(sipping his mug)
Stand here a minute. The light's good.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He's just standing. Not doing anything. You rarely see him like this.<</narrative>>
<<dialog "player">>You okay?<</dialog>>
<<dialog "father">>(surprised, not unhappy) Yeah. Just standing.
(looking at the window)
I woke up early and couldn't get back to sleep. So I'm just standing here.
(as if it's a revelation)
It's actually fine.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>He has his back slightly to you. Looking out the window at the morning, mug warm in both hands. He speaks without turning.<</narrative>>
<<dialog "father">>When I was young we moved twice. I hated it both times.
(a beat)
I decided I wouldn't feel that way here.
(turning, briefly)
How are you doing with it?<</dialog>>
<<dialog "player">>Getting used to it.<</dialog>>
<<dialog "father">>(back to the window) That's all it takes.<</dialog>>`,
friendship: 2, trust: 2
}
]
},
fhParentsRoom_preWork_evening: {
windingDown: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's sitting in the chair by the window, doing nothing in particular. It's late.<</narrative>>
<<dialog "player">>You're up late.<</dialog>>
<<dialog "father">>(quietly) Thinking.
(not elaborating)
Nothing bad. Just thinking.<</dialog>>
<<dialog "player">>Okay.<</dialog>>
<<dialog "father">>(after a moment) New place, new start. Takes some getting used to.
(back to looking at nothing)
It's getting there.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's in bed reading, door slightly open. He looks up when you knock.<</narrative>>
<<dialog "father">>What is it?<</dialog>>
<<dialog "player">>Nothing. Just passing. Goodnight.<</dialog>>
<<dialog "father">>(studying you briefly, checking) You alright?<</dialog>>
<<dialog "player">>Yeah. Fine. Night.<</dialog>>
<<dialog "father">>(settling back) Goodnight.
(a beat)
Sleep well.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>He's put the book down and the light's still on. He often goes to sleep thinking rather than reading.<</narrative>>
<<dialog "father">>(unexpectedly) Start date's coming up fast.
(to you, in the doorway)
I keep making lists and then thinking of more things.<</dialog>>
<<dialog "player">>That's very you.<</dialog>>
<<dialog "father">>(the faintest smile) Yes. It is.
(closing the notepad)
Goodnight.<</dialog>>`,
friendship: 2, trust: 1
}
],
quiet: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's just getting into bed when he hears you in the hallway. He calls softly.<</narrative>>
<<dialog "father">>Hey.<</dialog>>
<<dialog "player">>(stopping) Yeah?<</dialog>>
<<dialog "father">>Nothing. Just goodnight.
(simply)
Good day today.<</dialog>>
<<dialog "player">>Yeah. It was.<</dialog>>
<<dialog "father">>(turning off the light) Good.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's already in bed and half-asleep when you pass. You only went to the door but he stirs.<</narrative>>
<<dialog "father">>(groggy) S'everything alright?<</dialog>>
<<dialog "player">>Yeah. Sorry go back to sleep.<</dialog>>
<<dialog "father">>(already fading) Right.
(quietly)
Night.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>He's in the chair with the lamp on, not really reading. Just thinking with a book in his lap.<</narrative>>
<<dialog "father">>Come in a second.
(gesturing to the edge of the bed)
Sit down. I won't keep you.<</dialog>>
<<dialog "player">>(sitting) What's up?<</dialog>>
<<dialog "father">>Nothing. I just wanted the company for a minute.
(back to looking at the wall)
That's all.<</dialog>>`,
friendship: 2, trust: 1
}
],
tomorrow: [
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's writing something in his notepad. Tomorrow's list, probably. He looks up when you knock.<</narrative>>
<<dialog "father">>What do you need?<</dialog>>
<<dialog "player">>Nothing. Just saying goodnight.<</dialog>>
<<dialog "father">>(setting the pad down) Goodnight.
(a beat, studying you)
Big day tomorrow for you?<</dialog>>
<<dialog "player">>Not really. Just a day.<</dialog>>
<<dialog "father">>(back to his list) Good. Those are the best kind.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He's at the small desk in the corner, writing something out by hand. Unusual for him in the evening.<</narrative>>
<<dialog "player">>What are you writing?<</dialog>>
<<dialog "father">>(without looking up) Notes for myself. Things I want to remember before work starts.
(a pause)
Things I don't want to forget how to do.<</dialog>>
<<dialog "player">>Like what?<</dialog>>
<<dialog "father">>(brief, honest) Sit still. Be present. Answer questions with more than five words.
(slight wryness)
Work in progress.<</dialog>>`,
friendship: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/father/prework/level1/parentsRoomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>He closes the notepad and looks around the new room. Still slightly unfamiliar.<</narrative>>
<<dialog "father">>I think this is going to be good. The job. The house.
(looking at you in the doorway)
All of it.<</dialog>>
<<dialog "player">>Yeah?<</dialog>>
<<dialog "father">>(a simple, rare certainty) Yeah. I think so.
(picking up his book)
Goodnight.<</dialog>>`,
friendship: 2, trust: 1, love: 1
}
]
}
}>><<set $hideTopbarNav = true>>
<<set $hideTopbarTimebox = true>>
<<set $hideTopbarNotifications = true>>
<<set $hideRightbar = true>>
<<set $hideTopbar = true>>
<<set $hideTopbarHamburger = true>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="seductive-landing">
<!-- Silhouette Background -->
<div class="silhouette-layer">
<div class="silhouette silhouette-1"></div>
<div class="silhouette silhouette-2"></div>
</div>
<!-- Pink Glow Effects -->
<div class="mystery-glow glow-1"></div>
<div class="mystery-glow glow-2"></div>
<!-- Content -->
<div class="mystery-content">
<div class="mystery-tagline">
Where desire meets consequence
</div>
<h1 class="mystery-question">
What kind of person<br>
will you become?
</h1>
<div class="mystery-hint">
Every choice matters. Every relationship changes you.
</div>
<button class="btn-style btn-mystery-enter" id="welcomeEnterBtn">Enter</button>
<div class="mystery-warning">18+ · Adult Content</div>
</div>
<!-- Age Verification Modal -->
<div class="age-modal" id="ageModal">
<div class="age-modal-overlay" id="ageModalOverlay"></div>
<div class="age-modal-content">
<div class="age-modal-icon">🔞</div>
<h2 class="age-modal-title">Adult Content</h2>
<p class="age-modal-text">
This experience contains explicit adult content including sexual themes,
mature situations, and strong language.
</p>
<p class="age-modal-confirm">
By continuing, you confirm that you are <strong>18 years or older</strong>
and consent to view adult material.
</p>
<div class="age-modal-actions">
<<btn "I Accept" "settingsPage" "age-accept">><</btn>>
<a href="https://www.google.com" class="btn-style btn-age-decline">Exit</a>
</div>
</div>
</div>
</div>
<<script>>
$(document).ready(function() {
$('#welcomeEnterBtn').on('click', function() {
$('#ageModal').addClass('active');
$('body').css('overflow', 'hidden');
});
$('#ageModalOverlay').on('click', function() {
$('#ageModal').removeClass('active');
$('body').css('overflow', 'auto');
});
});
// Cleanup when leaving this passage
$(document).one(':passagestart', function(ev) {
// Only run cleanup if we're leaving welcomePage
if (ev.passage && ev.passage.title !== 'welcomePage') {
// Remove any inline styles added by modal
$('body').css('overflow', '');
// Remove fullscreen-centered class
$('body').removeClass('fullscreen-centered');
}
});
<</script>>/*
District hub ambient btnPicker 5th: "walk" | "phone" | "watch".
ENERGY drain/h (defaults): walk 10, phone 8, watch 10 → drain = max(1,round(rate*m/60)); START needs drain+10. Override: setup.hubAmbientEnergyDrainPerHour.
Per hour (m = minutes, scale with m/60):
Walk: stress −6, mood +8, energy −10, heels m, time m
Phone: stress −4, mood +8 (setup.hubAmbientMoodGainPerHour.phone), energy −8, time m
Watch: stress −8, mood +6, energy −10, time m
Flow: <<advanceTime m "relax">> first, then <<loseStat "stress">> / <<gainStat "mood">> / <<loseStat "energy">> (widgets queue via <<queueStatChange>>), <<recalculateStats>>, <<flushNotifications>>.
*/
<<script>>
(function () {
if (!setup.durationPresets) setup.durationPresets = {};
var _hubDurPresets = {
hubAmbientWalk: [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
],
hubAmbientPhone: [
{ label: "10 min", value: 10 },
{ label: "15 min", value: 15 },
{ label: "20 min", value: 20 },
{ label: "30 min", value: 30 }
],
hubAmbientWatch: [
{ label: "15 min", value: 15 },
{ label: "30 min", value: 30 },
{ label: "45 min", value: 45 },
{ label: "1 hour", value: 60 }
]
};
Object.keys(_hubDurPresets).forEach(function (key) {
if (!setup.durationPresets[key]) setup.durationPresets[key] = _hubDurPresets[key];
});
setup.hubAmbientEnergyDrainPerHour = Object.assign(
{ walk: 10, phone: 8, watch: 10 },
setup.hubAmbientEnergyDrainPerHour || {}
);
setup.hubAmbientMoodGainPerHour = Object.assign(
{ walk: 8, phone: 8, watch: 6 },
setup.hubAmbientMoodGainPerHour || {}
);
function _hubActDrain(key, mins) {
var h = setup.hubAmbientEnergyDrainPerHour[key];
var defM = key === 'phone' ? 10 : 15;
var m = parseInt(mins, 10);
var mm = !isNaN(m) && m > 0 ? m : defM;
return Math.max(1, Math.round((h * mm) / 60));
}
if (!setup.hubAmbientEnergyForMins) setup.hubAmbientEnergyForMins = {};
setup.hubAmbientEnergyForMins.walk = function (mins) {
return _hubActDrain('walk', mins) + 10;
};
setup.hubAmbientEnergyForMins.phone = function (mins) {
return _hubActDrain('phone', mins) + 10;
};
setup.hubAmbientEnergyForMins.watch = function (mins) {
return _hubActDrain('watch', mins) + 10;
};
/* Living room TV (watchTV.twee): base 2, +1 per extra 15m, max 6; +10 to start */
setup.hubAmbientEnergyForMins.tvWatch = function (mins) {
var m = parseInt(mins, 10) || 15;
var cost = 2;
if (m > 15) {
cost = 2 + Math.floor((m - 15) / 15) * 1;
cost = Math.min(6, cost);
}
return cost + 10;
};
if (setup.hubAmbient) return;
setup.hubAmbient = {
walk: {
maplewood: [
"You wander the quiet streetslawns, mailboxes, the occasional dog walker. Nothing dramatic happens, and that is the point.",
"A loop past the same tidy yards; sprinklers click on somewhere. You count porch lights and let your breathing slow.",
"Someone waves from a driveway; you half-wave back. The suburb pretends everyone already knows each other.",
"You take the long way around the block. Kids' bikes lie abandoned on a lawn; the day feels ordinary in a good way."
],
downTown: [
"You ride the rhythm of the sidewalkssuits, tourists, buses sighing at corners. The city presses in until your shoulders unclench anyway.",
"Glass towers throw back the sky; you weave through crosswalks and food smells. Noise becomes a kind of white noise.",
"You walk with nowhere to be, letting crowds split around you. A busker misses a note; nobody boos.",
"Steam from a vent, honking, someone laughing too loud on a call. You keep moving until your legs remind you you are still in a body."
],
oldTown: [
"Cobblestones and old signs; you loop past shop windows and school-day crowds. The neighborhood feels lived-in, familiar.",
"You pass the bakery smell twice on purpose. Bells tinkle; a shopkeeper nods like you are a regular.",
"Brick alleys shorten the wind. Tourists consult maps; locals cut through without looking.",
"The historic plaque you have ignored a hundred times still says the same year. You read it anyway, slowing down."
],
hillcrest: [
"Wide drives, polished cars, clipped hedges. You walk like you belongor like you are invisibleand either way the air feels expensive.",
"A gardener trims a hedge into geometry. Water features murmur behind gates you will never enter.",
"You follow the curve of a private road and turn back. Nothing happens; that is the flex.",
"Leather-soled footsteps on someone else's sidewalk. You practice a neutral face while windows reflect money."
],
marinaBay: [
"Salt, gulls, and open sky between buildings. You stroll toward the water and back, letting the breeze do half the work.",
"Rigging clinks; a dog shakes seawater onto tourists. You taste metal and sunscreen in the air.",
"You walk the pier without buying anything. Waves erase the same line over and over.",
"A ferry horn makes everyone look up at once. You let the shared pause happen without joining it."
],
universityDistrict: [
"Campus spillover fills the sidewalksbackpacks, coffee cups, loud plans for tonight. You drift with the current.",
"Flyers peel from a pole; someone practices a presentation aloud. You step around a longboard and almost smile.",
"The quad is half empty, half loud. You cut through anyway, anonymous among deadlines.",
"A food truck line doubles back; you do not join. The smell of fries follows you two blocks."
],
southside: [
"Chain-link, loading bays, murals half finished. You keep moving; people notice, then look away. The edge keeps you alert.",
"A dog behind a fence does not stop barking until you are past. You do not speed upthat would read wrong.",
"You walk the long side of an empty lot. Glass crunches; nobody asks what you are doing here.",
"Two guys argue over a car hood; they pause when you pass. You keep your eyes forward and your pace even."
],
suburbs: [
"Long blocks and thin trees; fewer faces, more distance between doors. Your footsteps are the main sound for a while.",
"A garage door rattles shut; a TV glows behind thin curtains. You wonder who is awake this early.",
"You walk past the same rental sign three days in a row. The grass grows anyway.",
"Distant highway hum; closer, a sprinkler ticks. The outskirts pretend they are far from everything."
],
redLightCenter: [
"Neon bleed and bass from doorways; you do not stop anywhere, just move through. The spectacle is exhausting in a strange, almost funny way.",
"Someone tries a flyer; you decline without eye contact. The sidewalk is sticky in patches.",
"You take a wide arc around a cluster of loud laughter. High heels click; a car slows, then does not stop.",
"Pink and purple light washes your hands a wrong color. You count doorways until the block ends."
]
},
phone: {
maplewood: [
"You scroll on a quiet stoopfeeds, messages, nothing urgent. The suburb hums behind the glass.",
"A group chat argues about nothing; you react with the safest emoji. A bird lands close; you do not film it.",
"You fall into short videos until your thumb aches. When you look up, the light has shifted.",
"Signal is perfect; guilt is optional. You answer one message and leave three on read."
],
downTown: [
"Against a building ledge you thumb through apps while the city roars past. Your screen feels like a tiny room.",
"You pretend to check a map so you have a reason to stand still. Notifications stack; you clear them without reading.",
"Someone bumps you; you apologize to your phone. The crowd does not care.",
"Brightness maxed against glare. You find a podcast and let voices replace the street for a while."
],
oldTown: [
"Between older storefronts you lose twenty minutes to videos and group chats. Time slips in the nicest way.",
"You sit on a bench and doomscroll gently. Pigeons negotiate crumbs; you do not share.",
"Wi-Fi from a café leaks far enough. You order nothing; the barista does not notice.",
"An old classmate posts a life update; you feel a flicker of something and keep scrolling."
],
hillcrest: [
"Perfect signal, polished aesthetics on every profile. You half-watch luxury pass by in real life while the algorithm shows you more.",
"You zoom a photo of a watch you cannot afford. The storefront reflection agrees you are out of place.",
"A dating app pings; you swipe without hope. Valets ignore you; the phone does not.",
"You read a long article about money to feel informed. The words blur; the headline was enough."
],
marinaBay: [
"Sun glare on the screen; you tilt away and keep scrolling. Vacation energy without going anywhere.",
"You post a story with the water in the background. The filter makes the sky impossible.",
"Sand in your shoe grinds; you ignore it for one more clip. Seagulls sound like notifications.",
"Someone's Bluetooth speaker plays the same summer hit. Your feed plays a different one; both are loud."
],
universityDistrict: [
"Memes, campus gossip, a calendar reminder you dismiss. The student hive mind fits in your palm.",
"You half-listen to a lecture recording while liking posts. Multitasking feels productive for ninety seconds.",
"A study group chat explodes; you mute it. Peace returns; guilt follows.",
"You compare your schedule to everyone else's highlight reel. The phone gets warm in your hand."
],
southside: [
"You keep one eye up while you scrollhabit. The phone still wins most of your attention.",
"Low bars; pages load in chunks. You fill the gaps by watching who walks behind you in reflection.",
"Someone sells something in your DMs; you block without drama. The street sells louder things.",
"You watch a fight video twice, then feel stupid. Pocket the phone; the block is still there."
],
suburbs: [
"Sparse notifications, long-loading clips. The quiet makes every ping feel louder than it is.",
"You read the same three apps in rotation. A car passes once a minute; you look up each time.",
"Your cousin's baby photos get a heart. The algorithm serves you ads for things you joked about once.",
"You sit in a driveway chair like a throne. The screen is the only window that moves."
],
redLightCenter: [
"Ads get weird fast here. You switch apps, laugh once, and pretend you are not cataloging what you see.",
"You delete your search history out of reflex. The street already knows what is for sale.",
"A notification vibrates; you silence it mid-scroll. Neon turns your face a liar color.",
"You answer a harmless text while walking past a doorway bass drop. Normal life feels like a split screen."
]
},
watch: {
maplewood: [
"Neighbors, joggers, a dad teaching a kid to ride a bike. Small stories; you read them without staring.",
"A sprinkler arcs; a woman argues with a hose. Two dogs negotiate a meeting; owners pretend they are in charge.",
"The mail truck stops at every box; you learn the driver's rhythm. Someone jogs past twicelaps or lost?",
"Kids trade cards on a curb. You remember doing that somewhere else. The scene keeps going without you."
],
downTown: [
"Crosswalk ballets, arguments at cab windows, someone filming something pointless. The city rehearses itself in front of you.",
"A suit eats salad on a ledge like a performance. Tourists aim phones at a building you stopped noticing years ago.",
"Traffic cop whistles; nobody listens the first time. You watch the second whistle work like magic.",
"Street preacher, street dancer, street vendorthree volumes competing. You do not pick a winner."
],
oldTown: [
"Families, teens in uniform, shopkeepers on smoke breaks. You pick up rhythmswho belongs, who is passing through.",
"An old couple shares fries on a bench; ketchup on a chin becomes a love language.",
"School lets out in a wave of blazers. You step aside; they flow around you like water.",
"A shop door bells every minute. You guess which customers will buy; you are wrong half the time."
],
hillcrest: [
"Who arrives in what car, who is waited on first. Tiny status games play out while valets pretend not to judge.",
"A woman tips the attendant without looking. The smile she gives the car is warmer than the one for people.",
"Someone's dog wears a sweater worth more than your shoes. The dog does not care; neither do you, officially.",
"Lunch meetings in tinted windowsmouths moving, hands gesturing. You invent their drama from posture alone."
],
marinaBay: [
"Tourists, locals, boats and bodies in motion. You note who lingers, who hurries, who is performing leisure.",
"A couple argues in whispers; seagulls scream over them. The pier pretends it is romantic anyway.",
"Kids chase waves; parents chase kids. You track a single kite until it becomes a dot.",
"Someone films the sunset for followers; someone else watches with bare eyes. Both look busy."
],
universityDistrict: [
"Flirts, study fights, impromptu frisbee. Social life on display like a live feed with no filter.",
"Two friends debrief a lecture in overlapping sentences. You catch phrasesprofessor, unfair, quiz.",
"A guy practices a handshake alone near a trash can. You look away before the second attempt.",
"Someone cries on a phone; someone else pretends not to hear. Campus empathy has bandwidth limits."
],
southside: [
"Corners and doorways tell you where not to loiter. You still learn the blockfaces, habits, who watches back.",
"A bodega door buzzes every few minutes. You memorize who gets waved in and who waits.",
"Kids on bikes circle the same stretch. You are scenery; they are the show.",
"Someone fixes a car with the hood up; tools clink. No one offers help; no one asks."
],
suburbs: [
"Sparse traffic, delivery vans, someone always mowing a lawn somewhere. Subtle patterns in a place that pretends nothing happens.",
"The same walker passes at the same time as yesterday. Routine looks like personality from far away.",
"A teenager slams a screen door; silence returns slowly. You count how long until the next sound.",
"Amazon drops a box; a dog announces the apocalypse. The owner shushes both."
],
redLightCenter: [
"Queues, bouncers, couples negotiating boundaries in whispers. You observe without committingand file it all away.",
"A group laughs too loud for the sidewalk; heels scrape; a phone flashlight checks a face. The night has rules.",
"Someone steps out for air; smoke rises; the door sucks closed behind them. They look relieved and bored.",
"Neon reflects in a puddle; you watch the colors ripple. Cheap poetry, but it works."
]
}
};
})();
<</script>>
<<widget "hubAmbientPickFlavor">><<nobr>>
<<set _hubKind = $args[0]>>
<<set _hubDist = $hubAmbientDistrict || $location>>
<<set _hubList = setup.hubAmbient[_hubKind] && setup.hubAmbient[_hubKind][_hubDist]>>
<<if _hubList && _hubList.length > 0>>
<<set _flavor = _hubList[random(0, _hubList.length - 1)]>>
<<else>>
<<set _flavor = "You take a moment.">>
<</if>>
<</nobr>><</widget>>
<<widget "hubAmbientActions">><<nobr>>
<<set $hubAmbientReturn = passage()>>
<<set $hubAmbientDistrict = $location>>
<div class="location-actions hub-ambient-actions">
<<btnPicker "Walk Around" "districtHub_walkAround" "hubAmbientWalk" "default" "walk">>
<<btnPicker "Play with Phone" "districtHub_playPhone" "hubAmbientPhone" "default" "phone">>
<<btnPicker "Watch People" "districtHub_watchPeople" "hubAmbientWatch" "default" "watch">>
</div>
<</nobr>><</widget>>
<<script>>
(function () {
if (setup.streetAttentionPool) return;
setup.streetAttentionPool = [
{
id: "stranger_passing_couple",
text: "A couple passes close by, one of them glancing back a second too long before pretending they were not looking."
},
{
id: "bus_stop_double_take",
text: "At the bus stop, someone gives you a double-take, then quickly focuses on their phone like nothing happened."
},
{
id: "construction_workers",
text: "A couple of workers pause their chatter for a beat as you walk past, then resume once you are out of earshot."
}
];
})();
<</script>>
<<widget "calculateAttentionScore">><<nobr>>
<<set _attentionScore = 0>>
<<set _baseChance = 0>>
<<if !$gameSettings || $gameSettings.arousalAmbient is false>>
<<return>>
<</if>>
<<set _makeupScore = (($makeup && $makeup.state is not "off" && ($makeup.style || 0) > 0) ? Math.round((($makeup.style || 0) * 20) * ((($makeup.quality || 0) / 100))) : 0)>>
<<set _outfitExposure = 0>>
<<set _outfitSexiness = 0>>
<<set _slots = ["top", "bottom", "dress", "shoes", "bodysuit", "bra", "panty"]>>
<<set _seenItemIds = []>>
<<for _slot range _slots>>
<<set _itemId = ($wardrobe && $wardrobe.equipped) ? $wardrobe.equipped[_slot] : "">>
<<if _itemId && !_seenItemIds.includes(_itemId)>>
<<run _seenItemIds.push(_itemId)>>
<<set _item = setup.getClothingById ? setup.getClothingById(_itemId) : null>>
<<if _item>>
<<set _outfitExposure += Number(_item.exposureLevel || 0)>>
<<set _outfitSexiness += Number(_item.sexinessScore || 0)>>
<</if>>
<</if>>
<</for>>
<<set _attentionScore = (($looks || 0) * 0.45) + (($beauty || 0) * 0.20) + (_makeupScore * 0.15) + (_outfitExposure * 0.10) + (_outfitSexiness * 0.10)>>
<<set _attentionScore = Math.max(0, Math.min(100, _attentionScore))>>
<<set _baseChance = Math.max(0, Math.min(0.30, (_attentionScore - 30) / 100))>>
<</nobr>><</widget>>
<<widget "arousalAttentionEvent">><<nobr>>
<<set _streetAttentionTriggered = false>>
<<if !$gameSettings || $gameSettings.arousalAmbient is false>>
<<return>>
<</if>>
<<set _nowMinuteStamp = Math.floor(Date.UTC($timeSys.year, $timeSys.month - 1, $timeSys.day, $timeSys.hour, $timeSys.minute || 0, 0) / 60000)>>
<<set _streetVariantCooldownMins = 1440>>
<<if ($streetAttentionLastMinuteStamp || 0) > 0 && (_nowMinuteStamp - ($streetAttentionLastMinuteStamp || 0)) < 60>>
<<return>>
<</if>>
<<set _pool = setup.streetAttentionPool || []>>
<<if !_pool.length>>
<<return>>
<</if>>
<<if ndef $streetAttentionVariantLastMinute>><<set $streetAttentionVariantLastMinute = {}>><</if>>
<<set _variantEligible = []>>
<<for _variant range _pool>>
<<set _vLast = ($streetAttentionVariantLastMinute[_variant.id] || 0)>>
<<if !_vLast || ((_nowMinuteStamp - _vLast) >= _streetVariantCooldownMins)>>
<<run _variantEligible.push(_variant)>>
<</if>>
<</for>>
<<set _candidates = _variantEligible.length ? _variantEligible : _pool>>
<<set _picked = _candidates[random(0, _candidates.length - 1)]>>
<<if !_picked>>
<<return>>
<</if>>
<<set _variantId = _picked.id>>
<<set _variantText = _picked.text>>
<<set _conf = $confidence || 0>>
<<set _aGain = 0>>
<<set _mGain = 0>>
<<set _sGain = 0>>
<<if _conf < 30>>
<<set _aGain = 2>>
<<set _sGain = 2>>
<<elseif _conf <= 60>>
<<set _aGain = 3>>
<<set _mGain = 1>>
<<else>>
<<set _aGain = 4>>
<<set _mGain = 2>>
<</if>>
<<gainStat "arousal" _aGain>>
<<if _mGain > 0>><<gainStat "mood" _mGain>><</if>>
<<if _sGain > 0>><<gainStat "stress" _sGain>><</if>>
<<set $streetAttentionLastMinuteStamp = _nowMinuteStamp>>
<<set $streetAttentionLastVariant = _variantId>>
<<set $streetAttentionVariantLastMinute[_variantId] = _nowMinuteStamp>>
<<set _streetAttentionTriggered = true>>
<<run
if (window.showNotification) {
window.showNotification({
type: 'info',
message: "Street attention: " + State.temporary._variantText
});
}
>>
<</nobr>><</widget>>
<<widget "runStreetAttentionCheck">><<nobr>>
<<if !$gameSettings || $gameSettings.arousalAmbient is false>>
<<return>>
<</if>>
<<calculateAttentionScore>>
<<if State.random() < (_baseChance || 0)>>
<<arousalAttentionEvent>>
<</if>>
<</nobr>><</widget>>
/* ==========================================
ITEM DATABASE
All purchasable items in the game
Item Structure:
- id: unique identifier
- name: display name
- category: consumable, cosmetic, gift, academic, tool
- usageType: direct (use from inventory), passage (use in passages), passive (no use)
- desc: description
- price: cost in dollars
- image: path to item image
- effects: array of effects [{stat, value, type, duration}]
- hasTooltip: whether to show info icon in shop
- maxUses: (optional) uses per purchase; if set, 1 buy = maxUses in inventory. Omit for unlimited (e.g. comb, umbrella)
- singleInventory: (optional) if true, shop allows only one unit ever (one per cart line, cap in inventory; hide in shop when owned)
========================================== */
<<set setup.items = {
/* ==========================================
CONSUMABLES
Items that can be used directly from inventory
========================================== */
consumables: [
{
id: "energy_drink",
name: "Energy Drink",
category: "consumable",
usageType: "direct",
desc: "A carbonated beverage that provides an instant energy boost. Popular among students.",
price: 3.50,
image: "assets/content/items/consumables/energyDrink.webp",
effects: [
{ stat: "energy", value: 30, type: "instant" },
{ stat: "mood", value: 5, type: "temporary", duration: 2 }
],
hasTooltip: true,
},
{
id: "coffee",
name: "Coffee",
category: "consumable",
usageType: "direct",
desc: "Freshly brewed coffee. Helps you stay awake and focused.",
price: 2.50,
image: "assets/content/items/consumables/coffee.webp",
effects: [
{ stat: "energy", value: 25, type: "instant" },
{ stat: "focus", value: 5, type: "temporary", duration: 2 }
],
hasTooltip: true,
},
{
id: "coffee_takeaway_americano",
name: "Americano",
category: "consumable",
usageType: "direct",
desc: "Strong americano in a takeaway cup.",
price: 3.50,
image: "assets/content/items/consumables/coffee.webp",
effects: [
{ stat: "energy", value: 22, type: "instant" },
{ stat: "focus", value: 8, type: "temporary", duration: 2 }
],
hasTooltip: true
},
{
id: "coffee_takeaway_latte",
name: "Latte",
category: "consumable",
usageType: "direct",
desc: "Creamy latte for a calmer energy boost.",
price: 4.50,
image: "assets/content/items/consumables/coffee.webp",
effects: [
{ stat: "energy", value: 16, type: "instant" },
{ stat: "stress", value: -6, type: "instant" },
{ stat: "mood", value: 3, type: "temporary", duration: 2 }
],
hasTooltip: true
},
{
id: "coffee_takeaway_cappuccino",
name: "Cappuccino",
category: "consumable",
usageType: "direct",
desc: "Foamy cappuccino with balanced body and aroma.",
price: 4.50,
image: "assets/content/items/consumables/coffee.webp",
effects: [
{ stat: "energy", value: 20, type: "instant" },
{ stat: "mood", value: 4, type: "instant" }
],
hasTooltip: true
},
{
id: "coffee_takeaway_mocha",
name: "Mocha",
category: "consumable",
usageType: "direct",
desc: "Chocolate-kissed mocha in a paper cup.",
price: 5.00,
image: "assets/content/items/consumables/coffee.webp",
effects: [
{ stat: "energy", value: 18, type: "instant" },
{ stat: "mood", value: 8, type: "instant" },
{ stat: "stress", value: -4, type: "instant" }
],
hasTooltip: true
},
{
id: "coffee_takeaway_coldbrew",
name: "Cold Brew",
category: "consumable",
usageType: "direct",
desc: "Ice-cold slow brew with high caffeine kick.",
price: 4.50,
image: "assets/content/items/consumables/coffee.webp",
effects: [
{ stat: "energy", value: 28, type: "instant" },
{ stat: "focus", value: 10, type: "temporary", duration: 2 },
{ stat: "thirst", value: -12, type: "instant" }
],
hasTooltip: true
},
{
id: "sandwich",
name: "Sandwich",
category: "consumable",
usageType: "direct",
desc: "A filling sandwich with fresh ingredients.",
price: 5.50,
image: "assets/content/items/consumables/sandwich.webp",
effects: [
{ stat: "hunger", value: -40, type: "instant" },
{ stat: "energy", value: 10, type: "instant" }
],
hasTooltip: true,
},
{
id: "water_bottle",
name: "Water Bottle",
category: "consumable",
usageType: "direct",
desc: "Pure refreshing water.",
price: 1.00,
image: "assets/content/items/consumables/water.webp",
effects: [
{ stat: "thirst", value: -50, type: "instant" }
],
hasTooltip: true,
},
{
id: "chocolate_bar",
name: "Chocolate Bar",
category: "consumable",
usageType: "direct",
desc: "Sweet chocolate treat. Boosts mood instantly.",
price: 1.50,
image: "assets/content/items/consumables/chocolateBar.webp",
effects: [
{ stat: "hunger", value: -10, type: "instant" },
{ stat: "mood", value: 10, type: "instant" }
],
hasTooltip: true
},
{
id: "apple",
name: "Fresh Apple",
category: "consumable",
usageType: "direct",
desc: "A healthy snack option.",
price: 0.75,
image: "assets/content/items/consumables/apple.webp",
effects: [
{ stat: "hunger", value: -20, type: "instant" },
{ stat: "health", value: 2, type: "instant" }
],
hasTooltip: true
}
],
/* ==========================================
TOOLS
Reusable utility items
========================================== */
tools: [
{
id: "umbrella",
name: "Umbrella",
category: "tool",
usageType: "passive",
desc: "Keeps you dry during rainy weather.",
price: 12.00,
image: "assets/content/items/tools/umbrella.webp",
effects: [],
hasTooltip: false
},
{
id: "webcam",
name: "Webcam",
category: "tool",
usageType: "passive",
desc: "HD webcam for video calls and streaming.",
price: 149.00,
image: "assets/content/items/tools/webcamHD.webp",
effects: [],
hasTooltip: true
},
{
id: "laptop",
name: "Laptop",
category: "tool",
usageType: "passive",
desc: "Portable laptop for work, study, and entertainment.",
price: 799.00,
image: "assets/content/items/tools/laptop.webp",
effects: [],
hasTooltip: true
}
],
/* ==========================================
EQUIPMENT
Fitness and activity items
========================================== */
equipment: [
{
id: "yoga_mat",
name: "Yoga Mat",
category: "equipment",
usageType: "passive",
desc: "A non-slip yoga mat for practicing yoga at home or in the park.",
price: 24.00,
image: "assets/content/items/global/yogaMat.webp",
effects: [],
hasTooltip: true
}
],
/* ==========================================
COSMETICS
Beauty and personal care items (used in passages like mirror)
========================================== */
cosmetics: [
{
id: "makeup_kit",
name: "Makeup Kit",
category: "cosmetic",
usageType: "passage",
maxUses: 30,
desc: "Basic cosmetic set for daily use. Use at a mirror.",
price: 35.00,
image: "assets/content/items/cosmetics/makeupKit.webp",
effects: [
{ stat: "looks", value: 5, type: "temporary", duration: 4 }
],
hasTooltip: true
},
{
id: "portable_makeup",
name: "Portable Makeup",
category: "cosmetic",
usageType: "passage",
maxUses: 30,
desc: "Portable makeup set for daily use. Use at a mirror.",
price: 50.00,
image: "assets/content/items/cosmetics/portableMakeup.webp",
effects: [
{ stat: "looks", value: 5, type: "temporary", duration: 4 }
],
hasTooltip: true
},
{
id: "perfume",
name: "Perfume",
category: "cosmetic",
usageType: "direct",
maxUses: 30,
desc: "Designer fragrance that boosts confidence. Can be applied anytime.",
price: 60.00,
image: "assets/content/items/cosmetics/perfume.webp",
effects: [
{ stat: "charisma", value: 3, type: "temporary", duration: 6 }
],
hasTooltip: true
},
{
id: "comb",
name: "Comb",
category: "cosmetic",
usageType: "passage",
singleInventory: true,
desc: "A simple comb for brushing your hair. Unlimited use.",
price: 5.00,
image: "assets/content/items/cosmetics/comb.webp",
effects: [],
hasTooltip: true
},
{
id: "hair_cream",
name: "Hair Cream",
category: "cosmetic",
usageType: "passage",
maxUses: 30,
desc: "Hair care cream for healthy, shiny hair. 30 applications per bottle.",
price: 15.00,
image: "assets/content/items/cosmetics/hairCream.webp",
effects: [],
hasTooltip: true
},
{
id: "face_cream",
name: "Face Cream",
category: "cosmetic",
usageType: "passage",
maxUses: 30,
desc: "Skincare cream for healthy, clear skin. 30 applications per bottle.",
price: 15.00,
image: "assets/content/items/cosmetics/faceCream.webp",
effects: [],
hasTooltip: true
},
{
id: "toothpaste",
name: "Toothpaste",
category: "cosmetic",
usageType: "passage",
maxUses: 30,
desc: "Toothpaste for daily dental care. 30 uses per tube.",
price: 5.00,
image: "assets/content/items/cosmetics/toothpaste.webp",
effects: [],
hasTooltip: true
},
{
id: "wet_wipes",
name: "Wet Wipes",
category: "cosmetic",
usageType: "passage",
maxUses: 30,
desc: "Cleansing wipes to remove makeup.",
price: 6.00,
image: "assets/content/items/cosmetics/wetWipes.webp",
effects: [],
hasTooltip: true
}
],
/* ==========================================
BOOKS
One-time read, progress saved. Content: ReadingDatabase.
========================================== */
books: [
// ── PERSUASION SERIES ──
{
id: "book_quiet_pitch",
name: "The Quiet Pitch",
category: "reading",
usageType: "passage",
desc: "A beginner's guide to persuasion through listening and framing.",
price: 14.99,
image: "assets/content/items/books/book_quiet_pitch.webp",
effects: [],
hasTooltip: true
},
{
id: "book_never_back_down",
name: "Never Back Down",
category: "reading",
usageType: "passage",
desc: "Intermediate negotiation and high-stakes persuasion.",
price: 22.99,
image: "assets/content/items/books/book_never_back_down.webp",
effects: [],
hasTooltip: true
},
{
id: "book_power_of_words",
name: "The Power of Words",
category: "reading",
usageType: "passage",
desc: "Advanced linguistic strategy and authentic influence.",
price: 38.99,
image: "assets/content/items/books/book_power_of_words.webp",
effects: [],
hasTooltip: true
},
// ── CONVERSATION SERIES ──
{
id: "book_just_talk",
name: "Just Talk",
category: "reading",
usageType: "passage",
desc: "Foundational small talk and conversation skills for everyday social situations.",
price: 12.99,
image: "assets/content/items/books/book_just_talk.webp",
effects: [],
hasTooltip: true
},
{
id: "book_social_blueprint",
name: "The Social Blueprint",
category: "reading",
usageType: "passage",
desc: "Intermediate guide to group dynamics and social navigation.",
price: 21.99,
image: "assets/content/items/books/book_social_blueprint.webp",
effects: [],
hasTooltip: true
},
{
id: "book_mastering_connection",
name: "Mastering Connection",
category: "reading",
usageType: "passage",
desc: "Deep listening, authentic presence, and cross-cultural communication.",
price: 36.99,
image: "assets/content/items/books/book_mastering_connection.webp",
effects: [],
hasTooltip: true
},
// ── RESEARCH SERIES ──
{
id: "book_how_to_find_anything",
name: "How to Find Anything",
category: "reading",
usageType: "passage",
desc: "A beginner's guide to source evaluation, search strategy, and organizing information.",
price: 13.99,
image: "assets/content/items/books/book_how_to_find_anything.webp",
effects: [],
hasTooltip: true
},
{
id: "book_source_and_truth",
name: "Source & Truth",
category: "reading",
usageType: "passage",
desc: "Media literacy, bias detection, and building a personal research system.",
price: 24.99,
image: "assets/content/items/books/book_source_and_truth.webp",
effects: [],
hasTooltip: true
},
{
id: "book_information_age",
name: "The Information Age",
category: "reading",
usageType: "passage",
desc: "Advanced information architecture and analytical frameworks.",
price: 42.99,
image: "assets/content/items/books/book_information_age.webp",
effects: [],
hasTooltip: true
},
// ── PROBLEM SOLVING SERIES ──
{
id: "book_think_it_through",
name: "Think It Through",
category: "reading",
usageType: "passage",
desc: "Root cause analysis, brainstorming frameworks, and decision-making for everyday problems.",
price: 13.99,
image: "assets/content/items/books/book_think_it_through.webp",
effects: [],
hasTooltip: true
},
{
id: "book_lateral_mind",
name: "The Lateral Mind",
category: "reading",
usageType: "passage",
desc: "Reframing problems and lateral thinking techniques.",
price: 22.99,
image: "assets/content/items/books/book_lateral_mind.webp",
effects: [],
hasTooltip: true
},
{
id: "book_mind_unlocked",
name: "Mind Unlocked",
category: "reading",
usageType: "passage",
desc: "Systems thinking and multi-variable analysis for complex problems.",
price: 39.99,
image: "assets/content/items/books/book_mind_unlocked.webp",
effects: [],
hasTooltip: true
},
// ── GAMING SERIES ──
{
id: "book_level_up",
name: "Level Up",
category: "reading",
usageType: "passage",
desc: "Game theory basics, resource management, and decision-making under pressure.",
price: 14.99,
image: "assets/content/items/books/book_level_up.webp",
effects: [],
hasTooltip: true
},
{
id: "book_pro_gamer_mindset",
name: "Pro Gamer Mindset",
category: "reading",
usageType: "passage",
desc: "Competitive psychology, tilt control, and team dynamics.",
price: 23.99,
image: "assets/content/items/books/book_pro_gamer_mindset.webp",
effects: [],
hasTooltip: true
},
{
id: "book_competitive_edge",
name: "The Competitive Edge",
category: "reading",
usageType: "passage",
desc: "High-level strategy, opponent modeling, and mastery mindset.",
price: 34.99,
image: "assets/content/items/books/book_competitive_edge.webp",
effects: [],
hasTooltip: true
},
// ── COOKING SERIES ──
{
id: "book_from_scratch",
name: "From Scratch",
category: "reading",
usageType: "passage",
desc: "Kitchen basics, knife skills, and fundamental cooking techniques.",
price: 16.99,
image: "assets/content/items/books/book_from_scratch.webp",
effects: [],
hasTooltip: true
},
{
id: "book_kitchen_manual",
name: "The Kitchen Manual",
category: "reading",
usageType: "passage",
desc: "Intermediate techniques: braising, emulsification, and pan sauces.",
price: 24.99,
image: "assets/content/items/books/book_kitchen_manual.webp",
effects: [],
hasTooltip: true
},
{
id: "book_mastery_kitchen",
name: "Mastery in the Kitchen",
category: "reading",
usageType: "passage",
desc: "Flavor science, restaurant-quality execution, and original recipe creation.",
price: 44.99,
image: "assets/content/items/books/book_mastery_kitchen.webp",
effects: [],
hasTooltip: true
},
// ── EVERYDAY BOOKS ──
{
id: "book_stillwater",
name: "Stillwater",
category: "reading",
usageType: "passage",
desc: "A quiet guide to stoic living and present-moment thinking.",
price: 11.99,
image: "assets/content/items/books/book_stillwater.webp",
effects: [],
hasTooltip: true
},
{
id: "book_letters_never_sent",
name: "Letters Never Sent",
category: "reading",
usageType: "passage",
desc: "A literary fiction about unsent letters and unspoken feelings.",
price: 10.99,
image: "assets/content/items/books/book_letters_never_sent.webp",
effects: [],
hasTooltip: true
},
{
id: "book_curious_mind",
name: "The Curious Mind",
category: "reading",
usageType: "passage",
desc: "Pop science exploring everyday physics, habits, and the edges of knowledge.",
price: 15.99,
image: "assets/content/items/books/book_curious_mind.webp",
effects: [],
hasTooltip: true
}
],
/* ==========================================
MAGAZINES
Single use: one Read session consumes the copy. Content: ReadingDatabase.
========================================== */
magazines: [
{
id: "mag_think_quick",
name: "Think Quick",
category: "reading",
usageType: "passage",
desc: "Problem-solving focused issue. Single use.",
price: 3.50,
image: "assets/content/items/magazines/mag_think_quick.webp",
effects: [],
hasTooltip: true
},
{
id: "mag_small_talk",
name: "Small Talk Weekly",
category: "reading",
usageType: "passage",
desc: "Conversation tactics and social flow tips. Single use.",
price: 3.25,
image: "assets/content/items/magazines/mag_small_talk.webp",
effects: [],
hasTooltip: true
},
{
id: "mag_home_flavors",
name: "Home Flavors",
category: "reading",
usageType: "passage",
desc: "Quick recipes and kitchen tricks. Single use.",
price: 4.00,
image: "assets/content/items/magazines/mag_home_flavors.webp",
effects: [],
hasTooltip: true
},
{
id: "mag_social_edge",
name: "Social Edge",
category: "reading",
usageType: "passage",
desc: "Persuasion and negotiation style tips. Single use.",
price: 3.75,
image: "assets/content/items/magazines/mag_social_edge.webp",
effects: [],
hasTooltip: true
},
{
id: "mag_green_corner",
name: "Green Corner",
category: "reading",
usageType: "passage",
desc: "Beginner plant care and gardening routines. Single use.",
price: 3.00,
image: "assets/content/items/magazines/mag_green_corner.webp",
effects: [],
hasTooltip: true
},
{
id: "mag_makeup_studio",
name: "Makeup Studio",
category: "reading",
usageType: "passage",
desc: "Makeup techniques for daily looks. Single use.",
price: 3.75,
image: "assets/content/items/magazines/mag_makeup_studio.webp",
effects: [],
hasTooltip: true
},
{
id: "mag_creative_pulse",
name: "Creative Pulse",
category: "reading",
usageType: "passage",
desc: "Creative inspiration and visual thinking. Single use.",
price: 3.60,
image: "assets/content/items/magazines/mag_creative_pulse.webp",
effects: [],
hasTooltip: true
},
{
id: "mag_inner_drive",
name: "Inner Drive",
category: "reading",
usageType: "passage",
desc: "Discipline and consistency strategies. Single use.",
price: 3.60,
image: "assets/content/items/magazines/mag_inner_drive.webp",
effects: [],
hasTooltip: true
}
]
}>>
/* ==========================================
JAKE TALK TOPICS COMMON PHASE, LEVEL 1
phase === "common" && friendshipLevel === 1
CHARACTER: Jake, Waiter at Ruby's Diner (dinerWaitress3)
→ Easygoing, social, younger energy. Single.
→ Most approachable person in the diner.
→ Tone: Friendly, casual, sometimes joking.
COMMON L1 TONE:
→ Player is not employed, just visiting.
→ Jake treats everyone like a potential buddy.
→ Light topics: sports, weekend, people-watching, humor.
→ CRITICAL: No work talk, no orders. Just vibes.
CONTEXTS:
dinerRubys_common_afternoon
dinerRubys_common_evening
========================================== */
<<nobr>>
<<set setup.jakeTopics = setup.jakeTopics || {}>>
<<set setup.jakeTopics.common = setup.jakeTopics.common || {}>>
<<set setup.jakeTopics.common.level1 = {
dinerRubys_common_afternoon: {
sports_talk: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-1.webp">>
<<image _img "100%">>
<<narrative>>Jake leans against the counter, glancing at the TV mounted in the corner.<</narrative>>
<<dialog "dinerWaitress3">>You catch the game last night?<</dialog>>
<<dialog "player">>Bits and pieces. Fell asleep on the couch.<</dialog>>
<<dialog "dinerWaitress3">>Dude, you missed the best part. Overtime.<</dialog>>
<<dialog "player">>That's what everyone keeps telling me today.<</dialog>>
<<dialog "dinerWaitress3">>Because it's true. Look it up later.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-2.webp">>
<<image _img "100%">>
<<narrative>>Jake wipes a table while sneaking glances at the muted sports highlights on TV.<</narrative>>
<<dialog "dinerWaitress3">>They traded the best guy on the roster.<</dialog>>
<<dialog "player">>Which team are we talking about here?<</dialog>>
<<dialog "dinerWaitress3">>Does it matter? It's always the wrong move.<</dialog>>
<<dialog "player">>You sound personally hurt by this decision.<</dialog>>
<<dialog "dinerWaitress3">>I am. I'm grieving at work right now.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-3.webp">>
<<image _img "100%">>
<<narrative>>Jake taps the edge of the counter like a drumroll, grinning at nobody in particular.<</narrative>>
<<dialog "dinerWaitress3">>Thinking about signing up for a rec league.<</dialog>>
<<dialog "player">>What sport? Basketball? Soccer?<</dialog>>
<<dialog "dinerWaitress3">>Kickball. Don't judge me, it's intense.<</dialog>>
<<dialog "player">>I'm not judging. Maybe a little bit.<</dialog>>
<<dialog "dinerWaitress3">>Come watch sometime. You'll convert fast.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
people_watching: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-4.webp">>
<<image _img "100%">>
<<narrative>>Jake nods toward a couple sitting by the window, speaking low.<</narrative>>
<<dialog "dinerWaitress3">>Those two haven't said a word to each other.<</dialog>>
<<dialog "player">>Maybe they're just comfortable with silence.<</dialog>>
<<dialog "dinerWaitress3">>Nah. That's the 'we fought in the car' silence.<</dialog>>
<<dialog "player">>You've got a whole theory going on here.<</dialog>>
<<dialog "dinerWaitress3">>I see it every day. I'm basically a scientist.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-5.webp">>
<<image _img "100%">>
<<narrative>>Jake pauses mid-step, balancing a tray on one hand, watching the door.<</narrative>>
<<dialog "dinerWaitress3">>Bet you the next person through that door orders coffee.<</dialog>>
<<dialog "player">>That's not really a bold prediction here.<</dialog>>
<<dialog "dinerWaitress3">>Fine. Coffee and pie. Fifty cents says pie.<</dialog>>
<<dialog "player">>You're betting me with coins from the tip jar?<</dialog>>
<<dialog "dinerWaitress3">>It's called investing. Watch and learn.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-6.webp">>
<<image _img "100%">>
<<narrative>>Jake slides into the booth across from you for a second, pretending to check the table.<</narrative>>
<<dialog "dinerWaitress3">>That regular at table three tips in exact change. Every time.<</dialog>>
<<dialog "player">>Maybe that's all they've got on them.<</dialog>>
<<dialog "dinerWaitress3">>It's always exactly fifteen percent. He does the math.<</dialog>>
<<dialog "player">>At least he's consistent about it.<</dialog>>
<<dialog "dinerWaitress3">>Consistently cheap. But hey, he's polite about it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
food_chat: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-7.webp">>
<<image _img "100%">>
<<narrative>>Jake glances at the specials board, then back at you with a half-grin.<</narrative>>
<<dialog "dinerWaitress3">>James made a new soup today. I already had two bowls.<</dialog>>
<<dialog "player">>Is it actually good or are you just bored?<</dialog>>
<<dialog "dinerWaitress3">>Both. But mostly good. You should try it.<</dialog>>
<<dialog "player">>Maybe I will. What kind is it?<</dialog>>
<<dialog "dinerWaitress3">>No idea. Just tastes like warmth. Profound, right?<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-8.webp">>
<<image _img "100%">>
<<narrative>>Jake leans over the counter and steals a fry off a plate someone left too close to the edge.<</narrative>>
<<dialog "dinerWaitress3">>That table left half their fries. Criminal.<</dialog>>
<<dialog "player">>You just took evidence from a crime scene.<</dialog>>
<<dialog "dinerWaitress3">>I'm performing a cleanup. Totally different.<</dialog>>
<<dialog "player">>Sure. Very professional.<</dialog>>
<<dialog "dinerWaitress3">>I'll write it up in my report. One fry, consumed, no survivors.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-common-9.webp">>
<<image _img "100%">>
<<narrative>>Jake is cutting a slice of pie with unnecessary precision, tongue slightly out in concentration.<</narrative>>
<<dialog "dinerWaitress3">>The pie slices have to be even. Vince counts them.<</dialog>>
<<dialog "player">>He actually counts pie slices?<</dialog>>
<<dialog "dinerWaitress3">>Every night. Found out the hard way.<</dialog>>
<<dialog "player">>What happened?<</dialog>>
<<dialog "dinerWaitress3">>Let's just say I owe the diner three slices of pie and move on.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
},
dinerRubys_common_evening: {
weekend_plans: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-common-1.webp">>
<<image _img "100%">>
<<narrative>>Jake stretches his arms overhead while walking between tables.<</narrative>>
<<dialog "dinerWaitress3">>Got any plans this weekend or just winging it?<</dialog>>
<<dialog "player">>Nothing solid yet. Probably just taking it easy.<</dialog>>
<<dialog "dinerWaitress3">>That's the best kind of plan honestly.<</dialog>>
<<dialog "player">>What about you? Something lined up?<</dialog>>
<<dialog "dinerWaitress3">>Buddy's having a thing. Might show up, might not.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-common-2.webp">>
<<image _img "100%">>
<<narrative>>Jake checks his phone quickly behind the counter, then pockets it.<</narrative>>
<<dialog "dinerWaitress3">>My friend keeps texting me about a bar crawl.<</dialog>>
<<dialog "player">>Sounds like it could be a good time.<</dialog>>
<<dialog "dinerWaitress3">>Or a terrible time. Same difference with him.<</dialog>>
<<dialog "player">>Those are usually the best stories afterward.<</dialog>>
<<dialog "dinerWaitress3">>That's exactly what I told him. Word for word.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-common-3.webp">>
<<image _img "100%">>
<<narrative>>Jake refills a napkin holder but keeps glancing toward the clock on the wall.<</narrative>>
<<dialog "dinerWaitress3">>Almost done here. Weekend starts in two hours.<</dialog>>
<<dialog "player">>Counting down the minutes already?<</dialog>>
<<dialog "dinerWaitress3">>I started counting at noon. This is just the final stretch.<</dialog>>
<<dialog "player">>Impressive commitment to not working.<</dialog>>
<<dialog "dinerWaitress3">>It's a lifestyle. I've perfected the art.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
light_humor: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-common-4.webp">>
<<image _img "100%">>
<<narrative>>Jake slides a ketchup bottle across the counter like a hockey puck, catching it at the edge.<</narrative>>
<<dialog "dinerWaitress3">>I've been practicing that move all shift.<</dialog>>
<<dialog "player">>What happens when you miss and it hits the floor?<</dialog>>
<<dialog "dinerWaitress3">>Already happened twice. James almost killed me.<</dialog>>
<<dialog "player">>And you're still doing it. Bold choice.<</dialog>>
<<dialog "dinerWaitress3">>Third time's the charm. You saw it work.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-common-5.webp">>
<<image _img "100%">>
<<narrative>>Jake holds up a receipt and squints at it dramatically.<</narrative>>
<<dialog "dinerWaitress3">>Someone wrote 'great smile' on the receipt instead of a tip.<</dialog>>
<<dialog "player">>That's kind of flattering though, isn't it?<</dialog>>
<<dialog "dinerWaitress3">>Flattering doesn't pay for gas. But yeah, I smiled.<</dialog>>
<<dialog "player">>At least your charm is being acknowledged somewhere.<</dialog>>
<<dialog "dinerWaitress3">>I'm framing this one. Hanging it on my fridge.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-common-6.webp">>
<<image _img "100%">>
<<narrative>>Jake pretends to take an invisible order from an empty booth, talking to nobody.<</narrative>>
<<dialog "dinerWaitress3">>Just rehearsing for the dinner crowd. Gotta stay sharp.<</dialog>>
<<dialog "player">>You look insane doing that by the way.<</dialog>>
<<dialog "dinerWaitress3">>Hey, the invisible customer loved my service. Five stars.<</dialog>>
<<dialog "player">>Did the invisible customer leave a real tip at least?<</dialog>>
<<dialog "dinerWaitress3">>Invisible tip. Best one I've gotten all day honestly.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>>/* ==========================================
JAKE TALK TOPICS WORKING DONE, LEVEL 1
phase === "dishwasherDone" && friendshipLevel === 1
CHARACTER: Jake, Waiter (dinerWaitress3)
→ Energetic, social, focused on plans after work.
→ Off the clock, winding down but still has energy.
========================================== */
<<nobr>>
<<set setup.jakeTopics = setup.jakeTopics || {}>>
<<set setup.jakeTopics.dishwasherDone = setup.jakeTopics.dishwasherDone || {}>>
<<set setup.jakeTopics.dishwasherDone.level1 = {
dinerRubys_dishwasherDone_afternoon: {
afternoon_done_jake: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-done-13.webp">>
<<image _img "100%">>
<<narrative>>Jake drops his notepad on the counter with a satisfied grin. He looks ready to sprint out the door.<</narrative>>
<<dialog "dinerWaitress3">>Afternoon shift successfully survived. Any casualties?<</dialog>>
<<dialog "player">>Just a few broken plates in the back. No big deal.<</dialog>>
<<dialog "dinerWaitress3">>The usual, then. I'm heading out before Vince finds more work.<</dialog>>
<<dialog "player">>Smart move. Hide while you can.<</dialog>>
<<dialog "dinerWaitress3">>I'm already invisible. See ya!<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-done-14.webp">>
<<image _img "100%">>
<<narrative>>Jake is loosening his tie, letting out a long sigh that sounds more like a whistle.<</narrative>>
<<dialog "dinerWaitress3">>This tie was strangling my creativity all afternoon.<</dialog>>
<<dialog "player">>Creativity? You were just taking burger orders, Jake.<</dialog>>
<<dialog "dinerWaitress3">>It's a performance art, $player.firstName. You wouldn't get it.<</dialog>>
<<dialog "player">>I'll stick to the dishes, then. Less pressure.<</dialog>>
<<dialog "dinerWaitress3">>Exactly. Enjoy the bubbles. I'm going to find some sun.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-done-15.webp">>
<<image _img "100%">>
<<narrative>>Jake checks his reflection in a spoon, smoothing down his hair. He looks quite pleased with himself.<</narrative>>
<<dialog "dinerWaitress3">>Still looking good after eight hours. It's a gift.<</dialog>>
<<dialog "player">>Modesty is also a gift, Jake. One you're missing.<</dialog>>
<<dialog "dinerWaitress3">>Why be modest when you're this efficient? See you tomorrow, $player.firstName.<</dialog>>
<<dialog "player">>Can't argue with that logic, I guess.<</dialog>>
<<dialog "dinerWaitress3">>Don't even try. It's foolproof.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-done-16.webp">>
<<image _img "100%">>
<<narrative>>Jake counts his section tables one more time, ticking off a mental checklist with satisfied nods.<</narrative>>
<<dialog "dinerWaitress3">>Okay, my section is absolutely spotless. Let the record show.<</dialog>>
<<dialog "player">>The record is noted.<</dialog>>
<<dialog "dinerWaitress3">>I want this acknowledged at the next staff meeting.<</dialog>>
<<dialog "player">>There are no staff meetings, Jake.<</dialog>>
<<dialog "dinerWaitress3">>Then I'll hold one. Just to acknowledge this.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-done-17.webp">>
<<image _img "100%">>
<<narrative>>Jake pulls his apron off in one clean motion and hangs it over a chair with ceremony.<</narrative>>
<<dialog "dinerWaitress3">>Watch this. Perfect apron hang. Every time.<</dialog>>
<<dialog "player">>Is that actually impressive?<</dialog>>
<<dialog "dinerWaitress3">>It's about the ritual, $player.firstName. The ritual.<</dialog>>
<<dialog "player">>What happens if it falls?<</dialog>>
<<dialog "dinerWaitress3">>It won't. I believe in myself too much.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-done-18.webp">>
<<image _img "100%">>
<<narrative>>Jake stretches his arms forward, cracking his knuckles loudly. He looks deeply satisfied.<</narrative>>
<<dialog "dinerWaitress3">>Afternoon shift complete. Officially a free man.<</dialog>>
<<dialog "player">>Until tomorrow morning at nine.<</dialog>>
<<dialog "dinerWaitress3">>Between now and nine tomorrow I am someone else entirely.<</dialog>>
<<dialog "player">>Who's that?<</dialog>>
<<dialog "dinerWaitress3">>Someone who doesn't carry a notepad. That's all I ask.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubys_dishwasherDone_evening: {
evening_done_jake: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-done-10.webp">>
<<image _img "100%">>
<<narrative>>Jake is leaning on the counter, watching the last customers linger. He looks tired but his eyes are still bright.<</narrative>>
<<dialog "dinerWaitress3">>The long night finally ends. You coming to the pub?<</dialog>>
<<dialog "player">>Not tonight, Jake. My bed is calling me.<</dialog>>
<<dialog "dinerWaitress3">>Weak! The night is just getting started.<</dialog>>
<<dialog "player">>Maybe for someone your age. I'm spent.<</dialog>>
<<dialog "dinerWaitress3">>Age is a state of mind. And mine is ready for a drink.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-done-11.webp">>
<<image _img "100%">>
<<narrative>>Jake is counting his tips, a small smile playing on his lips. He looks satisfied with the night's haul.<</narrative>>
<<dialog "dinerWaitress3">>People were generous tonight. Must be the moon.<</dialog>>
<<dialog "player">>Or your 'creative performance' worked on them.<</dialog>>
<<dialog "dinerWaitress3">>A bit of both, probably. I'm buying the first round tonight.<</dialog>>
<<dialog "player">>Enjoy it, Jake. You earned it.<</dialog>>
<<dialog "dinerWaitress3">>Every penny. Get some rest, $player.firstName.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-done-12.webp">>
<<image _img "100%">>
<<narrative>>Jake is putting on his hoodie, pulling the strings tight. He looks much more casual already.<</narrative>>
<<dialog "dinerWaitress3">>Hoodie on, work brain off. That's the rule.<</dialog>>
<<dialog "player">>What if I need to ask you about an order?<</dialog>>
<<dialog "dinerWaitress3">>Then you're talking to a ghost. I'm not here.<</dialog>>
<<dialog "player">>A very loud ghost in a red hoodie.<</dialog>>
<<dialog "dinerWaitress3">>I prefer 'stylishly ethereal.' See ya!<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-done-13.webp">>
<<image _img "100%">>
<<narrative>>Jake grabs his jacket from under the counter, shaking it out before putting it on.<</narrative>>
<<dialog "dinerWaitress3">>Jacket on. That's the signal.<</dialog>>
<<dialog "player">>Signal for what?<</dialog>>
<<dialog "dinerWaitress3">>Work brain fully offline.<</dialog>>
<<dialog "player">>And what comes online?<</dialog>>
<<dialog "dinerWaitress3">>Fun brain. Much better operating system.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-done-14.webp">>
<<image _img "100%">>
<<narrative>>Jake does a little shuffle step toward the exit, like he can't quite hold still now that the shift is done.<</narrative>>
<<dialog "dinerWaitress3">>My feet are literally walking without me.<</dialog>>
<<dialog "player">>You look like you need to run.<</dialog>>
<<dialog "dinerWaitress3">>I might actually run. Just to feel something.<</dialog>>
<<dialog "player">>Please don't run in the diner.<</dialog>>
<<dialog "dinerWaitress3">>Outside the diner. The moment I'm through that door.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-done-15.webp">>
<<image _img "100%">>
<<narrative>>Jake leans against the now-empty counter, looking out the window at the evening street.<</narrative>>
<<dialog "dinerWaitress3">>You know what's weird? Almost don't want to leave when it's quiet like this.<</dialog>>
<<dialog "player">>Almost?<</dialog>>
<<dialog "dinerWaitress3">>Almost. Emphasis on almost.<</dialog>>
<<dialog "player">>There's hope for you yet.<</dialog>>
<<dialog "dinerWaitress3">>Don't read into it. I'm out of here in sixty seconds.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
},
dinerRubysStorage_dishwasherDone_evening: {
storage_done_jake: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-done-7.webp">>
<<image _img "100%">>
<<narrative>>Jake is sitting on a stack of boxes in the storage room, finishing a soda. He looks peaceful in the dim light.<</narrative>>
<<dialog "dinerWaitress3">>Quiet at last. This room is a sanctuary after eight hours out there.<</dialog>>
<<dialog "player">>Is that why you're hiding back here instead of heading out?<</dialog>>
<<dialog "dinerWaitress3">>Just decompressing. Transitioning from 'waiter' to 'human.'<</dialog>>
<<dialog "player">>How's the human version doing?<</dialog>>
<<dialog "dinerWaitress3">>Thirsty. And very ready for the weekend.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-done-8.webp">>
<<image _img "100%">>
<<narrative>>Jake is tieing his shoes, his back against a shelf. He looks like he's gathering his energy for one last push.<</narrative>>
<<dialog "dinerWaitress3">>Last moment of calm before the storm of real life.<</dialog>>
<<dialog "player">>You make going home sound so dramatic, Jake.<</dialog>>
<<dialog "dinerWaitress3">>It's the transition, man. It's delicate.<</dialog>>
<<dialog "player">>Well, don't break anything on your way out.<</dialog>>
<<dialog "dinerWaitress3">>Just hearts and records, $player.firstName. Just hearts and records.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-done-9.webp">>
<<image _img "100%">>
<<narrative>>Jake is looking at his phone, the blue light catching his grin. He's probably texting someone about later.<</narrative>>
<<dialog "dinerWaitress3">>Plans are confirmed. Tonight's going to be legendary.<</dialog>>
<<dialog "player">>Don't do anything I wouldn't do.<</dialog>>
<<dialog "dinerWaitress3">>That leaves me a lot of room to play with, doesn't it?<</dialog>>
<<dialog "player">>I'm fairly boring, so yeah, probably.<</dialog>>
<<dialog "dinerWaitress3">>I'll live it up for both of us then. Later!<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-done-10.webp">>
<<image _img "100%">>
<<narrative>>Jake leans against the shelves, scrolling through his phone, already in after-work mode.<</narrative>>
<<dialog "dinerWaitress3">>Concert tonight. Small venue. You should come sometime.<</dialog>>
<<dialog "player">>What kind of music?<</dialog>>
<<dialog "dinerWaitress3">>Loud. You'd either love it or hate it immediately.<</dialog>>
<<dialog "player">>Only two options?<</dialog>>
<<dialog "dinerWaitress3">>With good music, yeah. No middle ground.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-done-11.webp">>
<<image _img "100%">>
<<narrative>>Jake tosses his apron into the laundry bin from across the room. It goes in.<</narrative>>
<<dialog "dinerWaitress3">>Kobe.<</dialog>>
<<dialog "player">>You've done that before.<</dialog>>
<<dialog "dinerWaitress3">>Every shift. Two for two this week.<</dialog>>
<<dialog "player">>And when you miss?<</dialog>>
<<dialog "dinerWaitress3">>I don't talk about those.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-done-12.webp">>
<<image _img "100%">>
<<narrative>>Jake glances toward the kitchen door, then relaxes when he hears nothing. He lets out a sigh.<</narrative>>
<<dialog "dinerWaitress3">>James is gone. I can hear myself think.<</dialog>>
<<dialog "player">>He really stresses you out.<</dialog>>
<<dialog "dinerWaitress3">>Not stress. More like... ambient tension. He's fine, just loud.<</dialog>>
<<dialog "player">>High praise from you.<</dialog>>
<<dialog "dinerWaitress3">>Don't tell him. I'll never hear the end of it.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
}
}>>
<</nobr>>/* ==========================================
JAKE TALK TOPICS WORKING ON BREAK PHASE, LEVEL 1
phase === "working.onBreak" && friendshipLevel === 1
CHARACTER: Jake, Waiter at Ruby's Diner (dinerWaitress3)
→ Easygoing, social, younger energy. Single.
→ Coworker mode casual, friendly, no tension.
→ Tone: Friendly, casual, sometimes joking.
WORKING ON BREAK L1 TONE:
→ Player is dishwasher, currently on break.
→ Jake is either still on the floor or also chilling.
→ Break topics: gym, dating, food, plans for later.
→ CRITICAL: Light coworker banter, not deep.
CONTEXTS:
dinerRubys_dishwasherOnBreak_afternoon
dinerRubys_dishwasherOnBreak_evening
dinerRubysStorage_dishwasherOnBreak_evening
========================================== */
<<nobr>>
<<set setup.jakeTopics = setup.jakeTopics || {}>>
<<set setup.jakeTopics.dishwasherOnBreak = setup.jakeTopics.dishwasherOnBreak || {}>>
<<set setup.jakeTopics.dishwasherOnBreak.level1 = {
dinerRubys_dishwasherOnBreak_afternoon: {
gym_talk: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Jake rolls his shoulder while carrying a bus tub past you.<</narrative>>
<<dialog "dinerWaitress3">>Hit the gym before my shift. Still feeling it.<</dialog>>
<<dialog "player">>What were you working on? Arms? Legs?<</dialog>>
<<dialog "dinerWaitress3">>Chest and back. Went a little too hard on rows.<</dialog>>
<<dialog "player">>That explains the shoulder rolling thing you're doing.<</dialog>>
<<dialog "dinerWaitress3">>Worth it though. Gains don't come free.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Jake flexes his forearm while wiping down the counter, grinning to himself.<</narrative>>
<<dialog "dinerWaitress3">>Do you work out or just wash dishes?<</dialog>>
<<dialog "player">>Dishes are my cardio at this point honestly.<</dialog>>
<<dialog "dinerWaitress3">>Bro, scrubbing pots is basically arm day. You're set.<</dialog>>
<<dialog "player">>I'll put that on my fitness app. 'Pot scrubs.'<</dialog>>
<<dialog "dinerWaitress3">>Patent that before someone else does. Seriously.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Jake stretches his neck side to side, taking a brief pause near the register.<</narrative>>
<<dialog "dinerWaitress3">>My gym buddy bailed on me this morning. Again.<</dialog>>
<<dialog "player">>That's the problem with gym buddies, they flake.<</dialog>>
<<dialog "dinerWaitress3">>He said his alarm didn't go off. Classic excuse.<</dialog>>
<<dialog "player">>At least you still went on your own.<</dialog>>
<<dialog "dinerWaitress3">>Always do. Can't let someone else ruin my streak.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
food_cravings: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-onBreak-4.webp">>
<<image _img "100%">>
<<narrative>>Jake eyes the specials board while wiping a nearby table.<</narrative>>
<<dialog "dinerWaitress3">>I've been craving tacos all day. This menu mocks me.<</dialog>>
<<dialog "player">>There's a truck down the block that's pretty good.<</dialog>>
<<dialog "dinerWaitress3">>The blue one? Their salsa is unreal.<</dialog>>
<<dialog "player">>That's the one. Get some after your shift.<</dialog>>
<<dialog "dinerWaitress3">>Already planning on it. You read my mind.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-onBreak-5.webp">>
<<image _img "100%">>
<<narrative>>Jake sniffs the air coming from the kitchen and makes a face.<</narrative>>
<<dialog "dinerWaitress3">>James is burning something again. Can you smell that?<</dialog>>
<<dialog "player">>Yeah, getting some smoky vibes back by the sink too.<</dialog>>
<<dialog "dinerWaitress3">>That's either the grill or someone's lunch. Pray for me.<</dialog>>
<<dialog "player">>If it's the grill, customers are going to notice.<</dialog>>
<<dialog "dinerWaitress3">>I'll tell them it's 'artisan char.' Fancy stuff.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontAfternoon-onBreak-6.webp">>
<<image _img "100%">>
<<narrative>>Jake pops a leftover fry into his mouth when nobody's looking.<</narrative>>
<<dialog "dinerWaitress3">>You didn't see that. That fry was already dead.<</dialog>>
<<dialog "player">>I saw nothing. Enjoy your stolen fry in peace.<</dialog>>
<<dialog "dinerWaitress3">>It's not stealing if it was heading to the trash anyway.<</dialog>>
<<dialog "player">>That's some solid moral reasoning right there.<</dialog>>
<<dialog "dinerWaitress3">>I've had time to think it through. Many fries ago.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
},
dinerRubys_dishwasherOnBreak_evening: {
dating_stories: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Jake leans on the counter, looking at his phone between customers.<</narrative>>
<<dialog "dinerWaitress3">>Got a match on that dating app. No idea what to say.<</dialog>>
<<dialog "player">>Just say something normal. Ask about their day.<</dialog>>
<<dialog "dinerWaitress3">>That's boring though. I need an opener.<</dialog>>
<<dialog "player">>Boring is safe. Safe gets replies back usually.<</dialog>>
<<dialog "dinerWaitress3">>Fine. 'Hey, how's your Tuesday.' Riveting stuff.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Jake shakes his head while refilling the sugar dispensers.<</narrative>>
<<dialog "dinerWaitress3">>Went on a date last week. Total disaster.<</dialog>>
<<dialog "player">>What happened? Bad conversation or bad food?<</dialog>>
<<dialog "dinerWaitress3">>Both. She ordered for me. Without asking.<</dialog>>
<<dialog "player">>That's a bold move from a stranger honestly.<</dialog>>
<<dialog "dinerWaitress3">>Bold is one word for it. I got a salad. A salad.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Jake straightens his collar in the reflection of the glass pie case.<</narrative>>
<<dialog "dinerWaitress3">>Do I look approachable or just tired right now?<</dialog>>
<<dialog "player">>Somewhere in between. Leaning toward tired honestly.<</dialog>>
<<dialog "dinerWaitress3">>That's not what I wanted to hear at all.<</dialog>>
<<dialog "player">>You asked for honesty. I delivered on that one.<</dialog>>
<<dialog "dinerWaitress3">>Fair enough. I'll splash some water on my face later.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
after_work_plans: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-onBreak-4.webp">>
<<image _img "100%">>
<<narrative>>Jake checks the time on his phone and slides it back into his apron pocket.<</narrative>>
<<dialog "dinerWaitress3">>Couple more hours and I'm free. Can't wait.<</dialog>>
<<dialog "player">>Got something planned or just heading home after?<</dialog>>
<<dialog "dinerWaitress3">>Probably meet up with some guys from school.<</dialog>>
<<dialog "player">>Sounds like a good way to end the night.<</dialog>>
<<dialog "dinerWaitress3">>Anything beats standing here, honestly. No offense to you.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-onBreak-5.webp">>
<<image _img "100%">>
<<narrative>>Jake drums his fingers on the counter, already mentally checked out.<</narrative>>
<<dialog "dinerWaitress3">>I'm thinking wings and a movie tonight. Thoughts?<</dialog>>
<<dialog "player">>Solid combo. What movie are you thinking about?<</dialog>>
<<dialog "dinerWaitress3">>Whatever's loudest. I need my brain to stop working.<</dialog>>
<<dialog "player">>Action movie it is then. Turn the brain off completely.<</dialog>>
<<dialog "dinerWaitress3">>Exactly. Pure explosions. Zero thinking required.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeFrontEvening-onBreak-6.webp">>
<<image _img "100%">>
<<narrative>>Jake yawns but catches himself, turning it into a grin.<</narrative>>
<<dialog "dinerWaitress3">>Man, this shift is dragging. You feel that too?<</dialog>>
<<dialog "player">>The last hour always feels like three somehow.<</dialog>>
<<dialog "dinerWaitress3">>Time is broken in this building. I'm convinced of that.<</dialog>>
<<dialog "player">>There's definitely something wrong with that clock up there.<</dialog>>
<<dialog "dinerWaitress3">>It's not the clock. It's the diner. It eats time.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
dinerRubysStorage_dishwasherOnBreak_evening: {
break_room_vibes: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Jake sits on a crate in the storage room, eating chips out of a bag.<</narrative>>
<<dialog "dinerWaitress3">>Best part of the shift right here. Chips and silence.<</dialog>>
<<dialog "player">>Where'd you even find those? The vending machine's broken.<</dialog>>
<<dialog "dinerWaitress3">>Stashed them behind the paper towels last week. Planning ahead.<</dialog>>
<<dialog "player">>That's either genius or deeply concerning behavior.<</dialog>>
<<dialog "dinerWaitress3">>Genius. Definitely genius. Want some?<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Jake leans back against the shelves, scrolling through his phone lazily.<</narrative>>
<<dialog "dinerWaitress3">>This back room smells like bleach and regret.<</dialog>>
<<dialog "player">>It's definitely seen better days. Or maybe not.<</dialog>>
<<dialog "dinerWaitress3">>Still better than being out there with Vince hovering.<</dialog>>
<<dialog "player">>He's been watching everyone pretty closely tonight.<</dialog>>
<<dialog "dinerWaitress3">>That's his hobby. We're the entertainment. Stay hidden.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Jake stretches his legs across a stack of supply boxes, arms behind his head.<</narrative>>
<<dialog "dinerWaitress3">>I could fall asleep back here. Seriously considering it.<</dialog>>
<<dialog "player">>Vince would lose his mind if he found you napping.<</dialog>>
<<dialog "dinerWaitress3">>That's the thrill though. Living on the edge every break.<</dialog>>
<<dialog "player">>Your definition of 'the edge' is pretty mild honestly.<</dialog>>
<<dialog "dinerWaitress3">>For a diner, this is extreme sports. Trust the process.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
coworker_gossip: [
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-onBreak-4.webp">>
<<image _img "100%">>
<<narrative>>Jake speaks quietly, glancing toward the storage room door.<</narrative>>
<<dialog "dinerWaitress3">>Emma almost snapped at a customer earlier. Close call.<</dialog>>
<<dialog "player">>What set her off? She's usually just quietly annoyed.<</dialog>>
<<dialog "dinerWaitress3">>Guy asked for a refill four times in ten minutes.<</dialog>>
<<dialog "player">>Okay, that would test anyone's patience honestly.<</dialog>>
<<dialog "dinerWaitress3">>She held it together. Barely. It was impressive.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-onBreak-5.webp">>
<<image _img "100%">>
<<narrative>>Jake peels the label off a water bottle, looking amused about something.<</narrative>>
<<dialog "dinerWaitress3">>Sofia convinced a customer his soup was 'artisan style.'<</dialog>>
<<dialog "player">>Was it not? What does that even mean here?<</dialog>>
<<dialog "dinerWaitress3">>It means James forgot to stir it. Had chunks floating in it.<</dialog>>
<<dialog "player">>And the customer actually bought that explanation. Wow.<</dialog>>
<<dialog "dinerWaitress3">>She's got a gift. I can't even be mad about it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/jake/level1/jakeStorageEvening-onBreak-6.webp">>
<<image _img "100%">>
<<narrative>>Jake crumples up a napkin and tries to toss it into a trash can across the room. Misses.<</narrative>>
<<dialog "dinerWaitress3">>Tom's been in a weird mood all evening. You notice?<</dialog>>
<<dialog "player">>He seemed quieter than usual out there, maybe.<</dialog>>
<<dialog "dinerWaitress3">>Probably his car again. That thing breaks down weekly.<</dialog>>
<<dialog "player">>That would put anyone in a mood for sure.<</dialog>>
<<dialog "dinerWaitress3">>I'd offer him a ride but last time he talked the whole way. Never again.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.jamesTopics = setup.jamesTopics || {}>>
<<set setup.jamesTopics.common = setup.jamesTopics.common || {}>>
<<set setup.jamesTopics.common.level1 = {
"dinerRubys_common_morning": {
"morning_prep": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontMorning-1.webp">>
<<image _img "100%">>
<<narrative>>James is hauling a crate of vegetables through the front, heading toward the kitchen. He barely glances up.<</narrative>>
<<dialog "dinerChef">>Morning deliveries. Excuse me.<</dialog>>
<<dialog "player">>Need a hand with that?<</dialog>>
<<dialog "dinerChef">>Nah, I got a system. You'd just slow it down.<</dialog>>
<<dialog "player">>Fair enough.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontMorning-2.webp">>
<<image _img "100%">>
<<narrative>>James stands near the counter checking a clipboard, pen behind his ear. His apron is still clean shift just started.<</narrative>>
<<dialog "dinerChef">>Inventory day. My favorite.<</dialog>>
<<dialog "player">>You don't sound thrilled.<</dialog>>
<<dialog "dinerChef">>Counting onions isn't exactly what I trained for.<</dialog>>
<<dialog "player">>What did you train for?<</dialog>>
<<dialog "dinerChef">>Cooking. Not paperwork.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontMorning-3.webp">>
<<image _img "100%">>
<<narrative>>James leans against the front counter with a coffee, staring at the empty dining room. It's still quiet.<</narrative>>
<<dialog "dinerChef">>Place is dead before noon. I like it.<</dialog>>
<<dialog "player">>Enjoy it while it lasts?<</dialog>>
<<dialog "dinerChef">>Exactly. Once the lunch crowd hits, it's nonstop.<</dialog>>
<<dialog "player">>Sounds intense.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"equipment_talk": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontMorning-4.webp">>
<<image _img "100%">>
<<narrative>>James is fiddling with the coffee machine behind the counter, muttering under his breath.<</narrative>>
<<dialog "dinerChef">>This thing leaks every other day.<</dialog>>
<<dialog "player">>Maybe it needs replacing.<</dialog>>
<<dialog "dinerChef">>Tell that to Vince. He thinks duct tape fixes everything.<</dialog>>
<<dialog "player">>Does it?<</dialog>>
<<dialog "dinerChef">>For about a week.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontMorning-5.webp">>
<<image _img "100%">>
<<narrative>>James examines a delivery receipt at the counter, comparing it to boxes stacked by the door.<</narrative>>
<<dialog "dinerChef">>They shorted us on buns again. Third time.<</dialog>>
<<dialog "player">>Can you get by without them?<</dialog>>
<<dialog "dinerChef">>I'll manage. Always do. Just annoying.<</dialog>>
<<dialog "player">>Sounds like a regular thing.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontMorning-6.webp">>
<<image _img "100%">>
<<narrative>>James wipes down the pass-through window between the front and kitchen, adjusting the heat lamp.<</narrative>>
<<dialog "dinerChef">>Heat lamp's dying. Food gets cold in two minutes.<</dialog>>
<<dialog "player">>That's not a lot of time.<</dialog>>
<<dialog "dinerChef">>It's an eternity when someone's waiting on a burger.<</dialog>>
<<dialog "player">>Never thought about it that way.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_common_afternoon": {
"lunch_rush": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>James appears at the pass-through window, sliding a plate onto the counter. Sweat beads on his forehead.<</narrative>>
<<dialog "dinerChef">>Order up. Table seven's been waiting.<</dialog>>
<<dialog "player">>Busy afternoon?<</dialog>>
<<dialog "dinerChef">>Every afternoon. You'd think people eat lunch at the same time every day or something.<</dialog>>
<<dialog "player">>Funny how that works.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>James is grabbing a bottle of ketchup from under the front counter, moving fast.<</narrative>>
<<dialog "dinerChef">>Running low on condiments out here. Figures.<</dialog>>
<<dialog "player">>Want me to move?<</dialog>>
<<dialog "dinerChef">>You're fine. Just grabbing this and heading back.<</dialog>>
<<dialog "player">>Kitchen keeping you busy?<</dialog>>
<<dialog "dinerChef">>Kitchen always keeps me busy.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>James steps out from the kitchen, wiping his hands on his apron. He takes a breath like he hasn't breathed in an hour.<</narrative>>
<<dialog "dinerChef">>Thirty-two orders since noon. Not bad.<</dialog>>
<<dialog "player">>Is that a lot?<</dialog>>
<<dialog "dinerChef">>For this place? Yeah. Usually more like twenty.<</dialog>>
<<dialog "player">>Sounds like a good day then.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"cooking_pride": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>James watches a customer take their first bite from across the room. He nods slightly to himself.<</narrative>>
<<dialog "dinerChef">>See that? Clean plate in five minutes. That's the goal.<</dialog>>
<<dialog "player">>You watch people eat your food?<</dialog>>
<<dialog "dinerChef">>Not in a weird way. Just... professional interest.<</dialog>>
<<dialog "player">>Sure, professional.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>James sets down a fresh plate at the pickup window a burger stacked perfectly, fries golden brown.<</narrative>>
<<dialog "dinerChef">>That right there. That's what I'm talking about.<</dialog>>
<<dialog "player">>Looks like a magazine cover.<</dialog>>
<<dialog "dinerChef">>It's a diner burger. It should look like a diner burger. Just a perfect one.<</dialog>>
<<dialog "player">>There's a difference?<</dialog>>
<<dialog "dinerChef">>Huge difference.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>James adjusts a plate on the counter, moving a pickle half an inch to the left. He catches you watching.<</narrative>>
<<dialog "dinerChef">>What? Presentation matters.<</dialog>>
<<dialog "player">>Even for a diner?<</dialog>>
<<dialog "dinerChef">>Especially for a diner. Nobody expects it. That's the point.<</dialog>>
<<dialog "player">>Respect.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_common_evening": {
"stretching_legs": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-1.webp">>
<<image _img "100%">>
<<narrative>>James leans against the counter near the register, rolling his shoulders. His apron is stained from the evening rush.<</narrative>>
<<dialog "dinerChef">>Kitchen's a furnace tonight. Had to get out for a second.<</dialog>>
<<dialog "player">>Smells good from here, at least.<</dialog>>
<<dialog "dinerChef">>That's the grill fat. Smells better than it tastes when it hits your arm.<</dialog>>
<<dialog "player">>Sounds rough.<</dialog>>
<<dialog "dinerChef">>Part of the job.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-2.webp">>
<<image _img "100%">>
<<narrative>>James stands by the window, looking out at the street. He cracks his knuckles one by one.<</narrative>>
<<dialog "dinerChef">>You eat here often?<</dialog>>
<<dialog "player">>Once in a while. The burgers are solid.<</dialog>>
<<dialog "dinerChef">>That's all me. Smash patty, nothing fancy.<</dialog>>
<<dialog "player">>Works every time.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-3.webp">>
<<image _img "100%">>
<<narrative>>James is wiping his hands on a towel, glancing at the dining area like he's inspecting it.<</narrative>>
<<dialog "dinerChef">>Table four's got crumbs everywhere. Nobody wiped it.<</dialog>>
<<dialog "player">>Not your department?<</dialog>>
<<dialog "dinerChef">>Everything's my department when it bugs me.<</dialog>>
<<dialog "player">>Fair enough.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"family_mention": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-4.webp">>
<<image _img "100%">>
<<narrative>>James checks his phone quickly, a small grin crossing his face before he pockets it.<</narrative>>
<<dialog "dinerChef">>Kid just sent me a drawing. Stick figure with a chef hat.<</dialog>>
<<dialog "player">>That's pretty cute.<</dialog>>
<<dialog "dinerChef">>Yeah. Gets the proportions wrong though. I'm not that fat.<</dialog>>
<<dialog "player">>Give it time.<</dialog>>
<<dialog "dinerChef">>Watch it.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-5.webp">>
<<image _img "100%">>
<<narrative>>James leans on the counter, staring at nothing in particular. He looks tired but content.<</narrative>>
<<dialog "dinerChef">>Wife wants me home by ten. Said the kids won't sleep without me reading to them.<</dialog>>
<<dialog "player">>That's sweet.<</dialog>>
<<dialog "dinerChef">>Sweet until you read the same book forty times.<</dialog>>
<<dialog "player">>Which book?<</dialog>>
<<dialog "dinerChef">>Something about a hungry caterpillar. Don't get me started.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-6.webp">>
<<image _img "100%">>
<<narrative>>James stretches his back with a grunt, one hand on the counter for support.<</narrative>>
<<dialog "dinerChef">>Promised my daughter I'd make pancakes tomorrow morning. She's been counting the days.<</dialog>>
<<dialog "player">>Chef at work and at home.<</dialog>>
<<dialog "dinerChef">>Home's different. She wants chocolate chips and whipped cream. Not exactly fine dining.<</dialog>>
<<dialog "player">>Sounds better than fine dining.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.jamesTopics = setup.jamesTopics || {}>>
<<set setup.jamesTopics.dishwasherDone = setup.jamesTopics.dishwasherDone || {}>>
<<set setup.jamesTopics.dishwasherDone.level1 = {
"dinerRubysKitchen_dishwasherDone_morning": {
"winding_down": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-7.webp">>
<<image _img "100%">>
<<narrative>>James scrapes the flattop with a metal spatula, working through the last of the morning's grease. The ticket rail is empty.<</narrative>>
<<dialog "dinerChef">>Morning shift's done. Finally.<</dialog>>
<<dialog "player">>Heading out soon?<</dialog>>
<<dialog "dinerChef">>Got cleanup first. Always cleanup.<</dialog>>
<<dialog "player">>Need a hand with anything?<</dialog>>
<<dialog "dinerChef">>Nah. Got my system. Thanks though.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-8.webp">>
<<image _img "100%">>
<<narrative>>James stacks clean pans on the shelf, moving with the slow deliberateness of someone who's been on their feet too long.<</narrative>>
<<dialog "dinerChef">>You done for the day too?<</dialog>>
<<dialog "player">>Yeah, just clocked out.<</dialog>>
<<dialog "dinerChef">>Good shift?<</dialog>>
<<dialog "player">>Busy, but nothing crazy.<</dialog>>
<<dialog "dinerChef">>That's the best kind.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-9.webp">>
<<image _img "100%">>
<<narrative>>James runs hot water over the cutting boards, steam rising around his hands. He glances over his shoulder.<</narrative>>
<<dialog "dinerChef">>Mornings go fast when it's busy.<</dialog>>
<<dialog "player">>Didn't feel fast to me.<</dialog>>
<<dialog "dinerChef">>That's because you watch the clock. I watch the food.<</dialog>>
<<dialog "player">>Different perspective, I guess.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"going_home": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-10.webp">>
<<image _img "100%">>
<<narrative>>James unties his apron and hangs it on the hook by the door. He rolls his shoulders back.<</narrative>>
<<dialog "dinerChef">>Might actually get home before noon today.<</dialog>>
<<dialog "player">>Big plans?<</dialog>>
<<dialog "dinerChef">>Couch. That's the plan.<</dialog>>
<<dialog "player">>Solid plan.<</dialog>>
<<dialog "dinerChef">>Best one I've had all week.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-11.webp">>
<<image _img "100%">>
<<narrative>>James checks his phone, then pockets it. He leans against the counter with a slow exhale.<</narrative>>
<<dialog "dinerChef">>Wife's picking up the kids. I've got an empty house for an hour.<</dialog>>
<<dialog "player">>That sounds like heaven.<</dialog>>
<<dialog "dinerChef">>You have no idea.<</dialog>>
<<dialog "player">>Enjoy the silence.<</dialog>>
<<dialog "dinerChef">>Plan to. Loudly.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-12.webp">>
<<image _img "100%">>
<<narrative>>James peels off his latex gloves and tosses them in the trash. He stretches his fingers wide.<</narrative>>
<<dialog "dinerChef">>Hands smell like onions. Gonna smell like onions all day.<</dialog>>
<<dialog "player">>Lemon juice helps with that.<</dialog>>
<<dialog "dinerChef">>I know. Just never remember to do it.<</dialog>>
<<dialog "player">>Keep a lemon by the door.<</dialog>>
<<dialog "dinerChef">>Then it'll go bad and I'll smell like old lemon and onions.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysKitchen_dishwasherDone_afternoon": {
"day_recap": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-7.webp">>
<<image _img "100%">>
<<narrative>>James dumps a load of dirty towels into a bin. The kitchen looks used but manageable.<</narrative>>
<<dialog "dinerChef">>Not bad today. Only one send-back.<</dialog>>
<<dialog "player">>What was wrong with it?<</dialog>>
<<dialog "dinerChef">>Lady wanted her burger well done. I gave her well done. She said it was too dry.<</dialog>>
<<dialog "player">>That's... what well done is.<</dialog>>
<<dialog "dinerChef">>Exactly. Thank you.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-8.webp">>
<<image _img "100%">>
<<narrative>>James writes something on the inventory clipboard, squinting at the shelves behind him.<</narrative>>
<<dialog "dinerChef">>Running low on buns again.<</dialog>>
<<dialog "player">>Busy lunch?<</dialog>>
<<dialog "dinerChef">>Every lunch is busy. That's the problem.<</dialog>>
<<dialog "player">>Isn't that a good problem?<</dialog>>
<<dialog "dinerChef">>Tell that to my bun supply.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-9.webp">>
<<image _img "100%">>
<<narrative>>James leans against the counter, arms crossed, watching the empty ticket rail with something like satisfaction.<</narrative>>
<<dialog "dinerChef">>Clean rail. Best sight in the kitchen.<</dialog>>
<<dialog "player">>Means you're done, right?<</dialog>>
<<dialog "dinerChef">>Means I'm caught up. Different thing.<</dialog>>
<<dialog "player">>Close enough though?<</dialog>>
<<dialog "dinerChef">>Close enough. I'll take it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"heading_out": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-10.webp">>
<<image _img "100%">>
<<narrative>>James pulls off his chef's cap and runs a hand through his flattened hair. He looks lighter already.<</narrative>>
<<dialog "dinerChef">>Afternoon shift. Done.<</dialog>>
<<dialog "player">>You look relieved.<</dialog>>
<<dialog "dinerChef">>I look like a guy who wants to sit down.<</dialog>>
<<dialog "player">>Same thing, honestly.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-11.webp">>
<<image _img "100%">>
<<narrative>>James packs his knife roll carefully, each blade going into its slot with practiced precision.<</narrative>>
<<dialog "dinerChef">>These go home with me. Don't trust the house knives.<</dialog>>
<<dialog "player">>You bring your own knives?<</dialog>>
<<dialog "dinerChef">>Every chef does. These are mine.<</dialog>>
<<dialog "player">>That's kind of cool, actually.<</dialog>>
<<dialog "dinerChef">>It's practical. But sure. Cool works too.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-12.webp">>
<<image _img "100%">>
<<narrative>>James checks the pilot lights on the stove, turning each burner off methodically. End-of-shift habit.<</narrative>>
<<dialog "dinerChef">>One time I left a burner on. Came back to a melted spatula.<</dialog>>
<<dialog "player">>Seriously?<</dialog>>
<<dialog "dinerChef">>Years ago. Never again.<</dialog>>
<<dialog "player">>Hence the triple-check.<</dialog>>
<<dialog "dinerChef">>Quadruple. But who's counting.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysKitchen_dishwasherDone_evening": {
"long_day": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>James sits on an overturned bucket, elbows on his knees. The kitchen is quiet for the first time all day.<</narrative>>
<<dialog "dinerChef">>Twelve hours. My feet hate me.<</dialog>>
<<dialog "player">>You did a double?<</dialog>>
<<dialog "dinerChef">>Covered for Mike. He owes me.<</dialog>>
<<dialog "player">>Get some rest tonight.<</dialog>>
<<dialog "dinerChef">>That's the only plan.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>James scrubs the last pot under the faucet, movements slow and mechanical. His eyes look heavy.<</narrative>>
<<dialog "dinerChef">>Last pot. Then I'm a free man.<</dialog>>
<<dialog "player">>Freedom looks good on you.<</dialog>>
<<dialog "dinerChef">>Freedom looks like my car seat right now.<</dialog>>
<<dialog "player">>Gonna fall asleep driving?<</dialog>>
<<dialog "dinerChef">>Windows down. Radio up. I'll manage.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>James wipes down the counter one final time, tossing the rag into the laundry bin. He surveys the clean kitchen.<</narrative>>
<<dialog "dinerChef">>Clean kitchen. That's how you leave it.<</dialog>>
<<dialog "player">>Looks good in here.<</dialog>>
<<dialog "dinerChef">>Better than when I got here. That's the standard.<</dialog>>
<<dialog "player">>Professional pride.<</dialog>>
<<dialog "dinerChef">>Something like that.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"evening_family": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenEvening-4.webp">>
<<image _img "100%">>
<<narrative>>James checks his phone, typing a quick message. The screen lights up his tired face.<</narrative>>
<<dialog "dinerChef">>Kids are already in bed. Missed bedtime again.<</dialog>>
<<dialog "player">>That's tough.<</dialog>>
<<dialog "dinerChef">>It is what it is. I'll see them in the morning.<</dialog>>
<<dialog "player">>They know you're working for them.<</dialog>>
<<dialog "dinerChef">>They're five and eight. They know I'm not there.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenEvening-5.webp">>
<<image _img "100%">>
<<narrative>>James pulls his jacket off the hook by the back door. He zips it up slowly.<</narrative>>
<<dialog "dinerChef">>Wife saved me a plate. She always does.<</dialog>>
<<dialog "player">>The chef gets cooked for. That's nice.<</dialog>>
<<dialog "dinerChef">>Her cooking's better than mine. Don't tell anyone.<</dialog>>
<<dialog "player">>Secret's safe.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenEvening-6.webp">>
<<image _img "100%">>
<<narrative>>James stands by the back door, keys in hand. He pauses like he's switching gears mentally from chef to dad.<</narrative>>
<<dialog "dinerChef">>Twenty-minute drive. Then I'm home.<</dialog>>
<<dialog "player">>Not bad.<</dialog>>
<<dialog "dinerChef">>It's the best part of the day. Just me and the road.<</dialog>>
<<dialog "player">>Decompression time?<</dialog>>
<<dialog "dinerChef">>Only quiet I get between here and the house.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherDone_afternoon": {
"off_the_clock": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-7.webp">>
<<image _img "100%">>
<<narrative>>James sits against the wall in the storage room, changed out of his chef whites into a plain t-shirt. He's eating an apple.<</narrative>>
<<dialog "dinerChef">>Done. Officially off the clock.<</dialog>>
<<dialog "player">>Why are you still here then?<</dialog>>
<<dialog "dinerChef">>Waiting for traffic to die down. Not stupid.<</dialog>>
<<dialog "player">>Smart move.<</dialog>>
<<dialog "dinerChef">>Give it twenty minutes. Then I'm gone.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-8.webp">>
<<image _img "100%">>
<<narrative>>James pulls a worn paperback from his bag, creasing it open to a dog-eared page. He glances up.<</narrative>>
<<dialog "dinerChef">>Don't say anything about the book.<</dialog>>
<<dialog "player">>I wasn't going to.<</dialog>>
<<dialog "dinerChef">>People always say something. "Oh, you read?"<</dialog>>
<<dialog "player">>What is it?<</dialog>>
<<dialog "dinerChef">>Western. Guy on a horse. That's all you need to know.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-9.webp">>
<<image _img "100%">>
<<narrative>>James sits on a stack of flour bags, scrolling through his phone. His chef shoes are off, replaced by beat-up sneakers.<</narrative>>
<<dialog "dinerChef">>Changed my shoes. Whole new person.<</dialog>>
<<dialog "player">>The sneakers do look more comfortable.<</dialog>>
<<dialog "dinerChef">>Kitchen shoes weigh a ton. These are like clouds.<</dialog>>
<<dialog "player">>The little things, right?<</dialog>>
<<dialog "dinerChef">>After eight hours? They're not little.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"shift_reflection": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-10.webp">>
<<image _img "100%">>
<<narrative>>James finishes a bottle of water and crushes it flat. He tosses it into the recycling bin from across the room.<</narrative>>
<<dialog "dinerChef">>Good day. No burns, no cuts, nothing went to waste.<</dialog>>
<<dialog "player">>That's a good day?<</dialog>>
<<dialog "dinerChef">>In a kitchen? That's a great day.<</dialog>>
<<dialog "player">>Low bar.<</dialog>>
<<dialog "dinerChef">>Realistic bar. There's a difference.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-11.webp">>
<<image _img "100%">>
<<narrative>>James leans back, hands behind his head. He looks like a different person out of the apron.<</narrative>>
<<dialog "dinerChef">>Tried something new with the special today.<</dialog>>
<<dialog "player">>How'd it go?<</dialog>>
<<dialog "dinerChef">>Sold out by one. So either good or cheap.<</dialog>>
<<dialog "player">>I'd say good.<</dialog>>
<<dialog "dinerChef">>I'd say both. But I'll take the compliment.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-12.webp">>
<<image _img "100%">>
<<narrative>>James packs his bag slowly, folding his chef coat neatly on top. He does everything with the same precision he cooks with.<</narrative>>
<<dialog "dinerChef">>Same time tomorrow. Same thing.<</dialog>>
<<dialog "player">>Doesn't that get old?<</dialog>>
<<dialog "dinerChef">>Routine's not the same as boring.<</dialog>>
<<dialog "player">>How do you mean?<</dialog>>
<<dialog "dinerChef">>I make the same dishes. Never the same way twice. Keeps it honest.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_dishwasherDone_evening": {
"closing_time": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-13.webp">>
<<image _img "100%">>
<<narrative>>James stands near the front door, jacket on, watching the last customers leave. The neon sign hums overhead.<</narrative>>
<<dialog "dinerChef">>Closing time. Best two words in English.<</dialog>>
<<dialog "player">>Long one tonight?<</dialog>>
<<dialog "dinerChef">>They're all long. This one just felt longer.<</dialog>>
<<dialog "player">>At least it's over.<</dialog>>
<<dialog "dinerChef">>Until tomorrow. Yeah.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-14.webp">>
<<image _img "100%">>
<<narrative>>James sits in one of the front booths something he never does during service. He looks strange on this side of the counter.<</narrative>>
<<dialog "dinerChef">>First time I've sat down in nine hours.<</dialog>>
<<dialog "player">>How does it feel?<</dialog>>
<<dialog "dinerChef">>Like my legs might not work when I stand up.<</dialog>>
<<dialog "player">>Take your time then.<</dialog>>
<<dialog "dinerChef">>Two minutes. Then I gotta drive.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-15.webp">>
<<image _img "100%">>
<<narrative>>James flips the "Open" sign to "Closed" and steps back. He shoves his hands in his pockets.<</narrative>>
<<dialog "dinerChef">>That's the move. Sign goes off. I go home.<</dialog>>
<<dialog "player">>Simple routine.<</dialog>>
<<dialog "dinerChef">>Best kind. No thinking required.<</dialog>>
<<dialog "player">>See you next shift?<</dialog>>
<<dialog "dinerChef">>I'll be here. Always am.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"night_drive": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-16.webp">>
<<image _img "100%">>
<<narrative>>James jingles his car keys, standing by the front counter. The diner is nearly empty.<</narrative>>
<<dialog "dinerChef">>You need a ride anywhere?<</dialog>>
<<dialog "player">>I'm good, thanks. Walking distance.<</dialog>>
<<dialog "dinerChef">>Lucky. I've got twenty minutes of highway.<</dialog>>
<<dialog "player">>At least no traffic this late.<</dialog>>
<<dialog "dinerChef">>Only good thing about the late shift.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-17.webp">>
<<image _img "100%">>
<<narrative>>James stares at the empty parking lot through the window. Streetlights cast orange over everything.<</narrative>>
<<dialog "dinerChef">>Quiet out there. I like it better at night.<</dialog>>
<<dialog "player">>The town or the diner?<</dialog>>
<<dialog "dinerChef">>Both. Everything slows down.<</dialog>>
<<dialog "player">>You don't seem like a slow-down kind of guy.<</dialog>>
<<dialog "dinerChef">>Everybody's got to stop moving eventually.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-18.webp">>
<<image _img "100%">>
<<narrative>>James zips his jacket and pulls out his phone, checking the time. He nods to himself.<</narrative>>
<<dialog "dinerChef">>Kids'll be asleep. House'll be dark.<</dialog>>
<<dialog "player">>Quiet entrance?<</dialog>>
<<dialog "dinerChef">>Like a ninja. Can't wake the little ones.<</dialog>>
<<dialog "player">>The chef turns ninja at night.<</dialog>>
<<dialog "dinerChef">>Whatever gets the job done.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.jamesTopics = setup.jamesTopics || {}>>
<<set setup.jamesTopics.dishwasherOnBreak = setup.jamesTopics.dishwasherOnBreak || {}>>
<<set setup.jamesTopics.dishwasherOnBreak.level1 = {
"dinerRubysKitchen_dishwasherOnBreak_morning": {
"tired_back": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>James leans against the prep counter, pressing a fist into his lower back. The morning rush left a mess of pans behind him.<</narrative>>
<<dialog "dinerChef">>Back's already killing me.<</dialog>>
<<dialog "player">>It's not even ten yet.<</dialog>>
<<dialog "dinerChef">>Yeah, thanks for the reminder.<</dialog>>
<<dialog "player">>You stretch before shifts?<</dialog>>
<<dialog "dinerChef">>I stretch getting out of bed. Doesn't help.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>James rolls his neck slowly, one hand braced on the stovetop edge. A half-eaten granola bar sits on the counter beside him.<</narrative>>
<<dialog "dinerChef">>Slept wrong. Neck's all jammed up.<</dialog>>
<<dialog "player">>You should get one of those pillows.<</dialog>>
<<dialog "dinerChef">>Got three. Wife keeps buying them.<</dialog>>
<<dialog "player">>And they don't work?<</dialog>>
<<dialog "dinerChef">>I sleep face down. Nothing works.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>James twists at the waist, hands on his hips. Something pops audibly.<</narrative>>
<<dialog "dinerChef">>That one's been stuck all morning.<</dialog>>
<<dialog "player">>That sounded awful.<</dialog>>
<<dialog "dinerChef">>Felt great though.<</dialog>>
<<dialog "player">>Whatever works, I guess.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"morning_rush": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-4.webp">>
<<image _img "100%">>
<<narrative>>James wipes his hands on his apron, surveying the pile of tickets still clipped to the rail.<</narrative>>
<<dialog "dinerChef">>Breakfast crowd doesn't quit.<</dialog>>
<<dialog "player">>Felt like nonstop out front too.<</dialog>>
<<dialog "dinerChef">>Eggs. Always eggs. Everybody wants eggs a different way.<</dialog>>
<<dialog "player">>Over easy's the popular one, right?<</dialog>>
<<dialog "dinerChef">>Scrambled. Always scrambled. Easiest thing on the menu.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-5.webp">>
<<image _img "100%">>
<<narrative>>James takes a long drink of water from a plastic cup, leaning against the walk-in cooler door.<</narrative>>
<<dialog "dinerChef">>Morning rush hit different today.<</dialog>>
<<dialog "player">>Yeah, we were slammed.<</dialog>>
<<dialog "dinerChef">>Some guy sent back his toast. Twice.<</dialog>>
<<dialog "player">>What was wrong with it?<</dialog>>
<<dialog "dinerChef">>Too toasted. Then not toasted enough. Pick a lane.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenMorning-6.webp">>
<<image _img "100%">>
<<narrative>>James rests both hands flat on the counter, head dipped forward. He exhales slowly.<</narrative>>
<<dialog "dinerChef">>Fifteen omelets. Fifteen.<</dialog>>
<<dialog "player">>Is that a lot for a morning?<</dialog>>
<<dialog "dinerChef">>It's a lot for one guy with two burners.<</dialog>>
<<dialog "player">>Fair point.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysKitchen_dishwasherOnBreak_afternoon": {
"quick_smoke": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>James fidgets with an unlit cigarette, tucking it behind his ear. He checks the clock on the wall.<</narrative>>
<<dialog "dinerChef">>Five minutes. That's all I need.<</dialog>>
<<dialog "player">>Sneaking out back?<</dialog>>
<<dialog "dinerChef">>Not sneaking. Just... going quickly.<</dialog>>
<<dialog "player">>Ruby doesn't mind?<</dialog>>
<<dialog "dinerChef">>Ruby doesn't need to know everything.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>James leans on the prep table, drumming his fingers. The afternoon lull has the kitchen quieter than usual.<</narrative>>
<<dialog "dinerChef">>Trying to cut back. Down to three a day.<</dialog>>
<<dialog "player">>That's progress, right?<</dialog>>
<<dialog "dinerChef">>Was ten last year. So yeah.<</dialog>>
<<dialog "player">>Keep at it.<</dialog>>
<<dialog "dinerChef">>Wife says the same thing. Less nice about it though.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>James pats his apron pocket, then stops himself. He sighs and grabs a toothpick instead.<</narrative>>
<<dialog "dinerChef">>Toothpick instead. Doctor's orders.<</dialog>>
<<dialog "player">>Does that actually help?<</dialog>>
<<dialog "dinerChef">>Keeps my mouth busy. That's half the habit.<</dialog>>
<<dialog "player">>Smart approach, honestly.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"afternoon_lull": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>James slowly sharpens a knife, the rhythm steady and practiced. The kitchen is calm between services.<</narrative>>
<<dialog "dinerChef">>This part's almost nice. The quiet.<</dialog>>
<<dialog "player">>Enjoy it while it lasts?<</dialog>>
<<dialog "dinerChef">>Dinner rush'll fix that real quick.<</dialog>>
<<dialog "player">>Anything big on the menu tonight?<</dialog>>
<<dialog "dinerChef">>Same as always. People don't come here for surprises.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>James wipes down the flattop grill with long, even strokes. He's unhurried for once.<</narrative>>
<<dialog "dinerChef">>Afternoon's the only sane part of this job.<</dialog>>
<<dialog "player">>Mornings and evenings that bad?<</dialog>>
<<dialog "dinerChef">>Mornings are chaos. Evenings are loud chaos.<</dialog>>
<<dialog "player">>And afternoons?<</dialog>>
<<dialog "dinerChef">>Quiet chaos. I'll take it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesKitchenAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>James refills the squeeze bottles methodically ketchup, mustard, hot sauce. Muscle memory.<</narrative>>
<<dialog "dinerChef">>Prep work. Most boring part.<</dialog>>
<<dialog "player">>At least it's easy though.<</dialog>>
<<dialog "dinerChef">>Easy and boring aren't the same thing.<</dialog>>
<<dialog "player">>Guess not.<</dialog>>
<<dialog "dinerChef">>But yeah. Beats the rush.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherOnBreak_afternoon": {
"kids_talk": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>James sits on a crate in the storage room, eating a sandwich he brought from home. He looks up when you walk in.<</narrative>>
<<dialog "dinerChef">>Kid's got a soccer game Saturday.<</dialog>>
<<dialog "player">>You gonna make it this time?<</dialog>>
<<dialog "dinerChef">>Swapped shifts. Better be worth it.<</dialog>>
<<dialog "player">>He'll be happy you're there.<</dialog>>
<<dialog "dinerChef">>She. And yeah. She better score.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>James scrolls through his phone, a half smile on his face. He tilts the screen slightly as you approach.<</narrative>>
<<dialog "dinerChef">>Look at this. Youngest one tried to make a sandwich.<</dialog>>
<<dialog "player">>How'd that go?<</dialog>>
<<dialog "dinerChef">>Peanut butter on the outside. Bread in the middle.<</dialog>>
<<dialog "player">>Creative approach.<</dialog>>
<<dialog "dinerChef">>That's one word for it.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>James leans against a shelf of canned goods, arms crossed. He looks like he's thinking about something far from the diner.<</narrative>>
<<dialog "dinerChef">>My oldest wants to be a chef now.<</dialog>>
<<dialog "player">>Following in your footsteps?<</dialog>>
<<dialog "dinerChef">>Told her to pick something easier.<</dialog>>
<<dialog "player">>She didn't listen, did she?<</dialog>>
<<dialog "dinerChef">>She's eight. Eight-year-olds don't listen.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"the_quiet": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>James stands in the dim storage room, eyes closed, just breathing. He opens them when the door creaks.<</narrative>>
<<dialog "dinerChef">>Two minutes of silence. That's all I wanted.<</dialog>>
<<dialog "player">>Sorry, I can go<</dialog>>
<<dialog "dinerChef">>Nah. You're quiet enough. Stay.<</dialog>>
<<dialog "player">>High praise from you.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>James stretches his arms overhead, nearly touching the low ceiling. He drops them with a sigh.<</narrative>>
<<dialog "dinerChef">>Storage room's the only cool spot in this place.<</dialog>>
<<dialog "player">>Kitchen that hot today?<</dialog>>
<<dialog "dinerChef">>Grill, fryer, oven. All going at once.<</dialog>>
<<dialog "player">>Sounds like a sauna.<</dialog>>
<<dialog "dinerChef">>Sauna without the relaxation part.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesStorageAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>James is leaning against the wall, coffee in hand, staring at nothing. He looks almost peaceful.<</narrative>>
<<dialog "dinerChef">>You ever just stand somewhere and think about nothing?<</dialog>>
<<dialog "player">>All the time, honestly.<</dialog>>
<<dialog "dinerChef">>Good. That's healthy. I read that somewhere.<</dialog>>
<<dialog "player">>Didn't take you for a reader.<</dialog>>
<<dialog "dinerChef">>Wife left the article on the counter. I read while I eat.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_dishwasherOnBreak_evening": {
"end_of_day": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-7.webp">>
<<image _img "100%">>
<<narrative>>James stands near the front counter, apron loosened. He watches the last few customers through the window.<</narrative>>
<<dialog "dinerChef">>Almost through. Just the closing push left.<</dialog>>
<<dialog "player">>Feels like today dragged, right?<</dialog>>
<<dialog "dinerChef">>Every day drags. Some just drag more.<</dialog>>
<<dialog "player">>Cheerful as always.<</dialog>>
<<dialog "dinerChef">>I save the cheer for my kids.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-8.webp">>
<<image _img "100%">>
<<narrative>>James stands by the register, arms folded. He's out of the kitchen unusual for him.<</narrative>>
<<dialog "dinerChef">>Needed a change of scenery. Even if it's just ten feet.<</dialog>>
<<dialog "player">>The front's almost exotic for you.<</dialog>>
<<dialog "dinerChef">>Don't push it.<</dialog>>
<<dialog "player">>You look lost without the grill.<</dialog>>
<<dialog "dinerChef">>I look fine. Mind your business.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-9.webp">>
<<image _img "100%">>
<<narrative>>James leans on the door frame between the kitchen and the front, half in both worlds.<</narrative>>
<<dialog "dinerChef">>Evening crowd's thinning out. Thank god.<</dialog>>
<<dialog "player">>Rough night on the line?<</dialog>>
<<dialog "dinerChef">>Nothing I can't handle. Just long.<</dialog>>
<<dialog "player">>You say that every shift.<</dialog>>
<<dialog "dinerChef">>Because it's true every shift.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"quick_breather": [
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-10.webp">>
<<image _img "100%">>
<<narrative>>James holds a glass of water, drinking it in steady gulps. He finishes and sets it down with a thud.<</narrative>>
<<dialog "dinerChef">>Forgot to drink water for three hours.<</dialog>>
<<dialog "player">>That can't be good for you.<</dialog>>
<<dialog "dinerChef">>Probably not. Do it every day though.<</dialog>>
<<dialog "player">>Set an alarm or something.<</dialog>>
<<dialog "dinerChef">>Yeah, I'll just check my phone mid-service. Great plan.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-11.webp">>
<<image _img "100%">>
<<narrative>>James cracks his knuckles one at a time, staring out the front windows at the dark street.<</narrative>>
<<dialog "dinerChef">>Hands cramp up by evening. Every time.<</dialog>>
<<dialog "player">>From the cooking?<</dialog>>
<<dialog "dinerChef">>From everything. Gripping, lifting, chopping.<</dialog>>
<<dialog "player">>You should soak them or something.<</dialog>>
<<dialog "dinerChef">>I run them under hot water when I do dishes. Close enough.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/james/level1/jamesFrontEvening-12.webp">>
<<image _img "100%">>
<<narrative>>James stands near the door, arms at his sides, just breathing the cooler air from outside.<</narrative>>
<<dialog "dinerChef">>Fresh air. Forgot what it smelled like.<</dialog>>
<<dialog "player">>Better than fryer grease?<</dialog>>
<<dialog "dinerChef">>Anything's better than fryer grease at hour eight.<</dialog>>
<<dialog "player">>Can't argue with that.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
/* ==========================================
JOB SYSTEM WIDGETS
Shift execution, pay day, weekly reset
Design: 1Developer/JobSystemDesign.md
========================================== */
/* Ensure job state fields exist on old saves before use. */
<<widget "jobEnsureState">>
<<if ndef $jobState>><<jobResetState>><<return>><</if>>
<<if ndef $jobState.workTrustScore>><<set $jobState.workTrustScore = 60>><</if>>
<<if ndef $jobState.workTrustBand>><<set $jobState.workTrustBand = "mid">><</if>>
<<if ndef $jobState.bossMeetingRequired>><<set $jobState.bossMeetingRequired = false>><</if>>
<<if ndef $jobState.bossMeetingReason>><<set $jobState.bossMeetingReason = "">><</if>>
<<if ndef $jobState.terminationPending>><<set $jobState.terminationPending = false>><</if>>
<<if ndef $jobState.promotionPending>><<set $jobState.promotionPending = false>><</if>>
<<if ndef $jobState.pendingTier>><<set $jobState.pendingTier = 0>><</if>>
<<if ndef $jobState.weeklyPayPending>><<set $jobState.weeklyPayPending = null>><</if>>
<<if ndef $jobState.lastInsufficientType>><<set $jobState.lastInsufficientType = "">><</if>>
<<if ndef $jobState.lastInsufficientHours>><<set $jobState.lastInsufficientHours = 0>><</if>>
<<if ndef $jobState.excuseForDateKey>><<set $jobState.excuseForDateKey = 0>><</if>>
<<if ndef $jobState.excuseMode>><<set $jobState.excuseMode = "">><</if>>
<<if ndef $jobState.excuseSentToday>><<set $jobState.excuseSentToday = false>><</if>>
<<if ndef $jobState.bossLieAttemptedThisMeeting>><<set $jobState.bossLieAttemptedThisMeeting = false>><</if>>
<<if ndef $jobState.bossLieLastResult>><<set $jobState.bossLieLastResult = "">><</if>>
<<if ndef $jobState.bossLieLastReason>><<set $jobState.bossLieLastReason = "">><</if>>
<<if ndef $jobState.bossLieLastKey>><<set $jobState.bossLieLastKey = "">><</if>>
<<if ndef $jobState.bossLieLastChance>><<set $jobState.bossLieLastChance = 0>><</if>>
<<if ndef $jobState.bossLieLastPenaltyAmount>><<set $jobState.bossLieLastPenaltyAmount = 0>><</if>>
<<if $job && $job.id && setup.jobs[$job.id]>>
<<set _jobTierMax = parseInt(setup.jobs[$job.id].tierMax, 10) || 6>>
<<set _curJobTier = parseInt($job.tier, 10) || 1>>
<<if _curJobTier > _jobTierMax>><<set $job.tier = _jobTierMax>><</if>>
<</if>>
<</widget>>
<<widget "jobSetTrustBand">>
<<set _trust = parseInt($jobState.workTrustScore, 10) || 0>>
<<if _trust >= 70>>
<<set $jobState.workTrustBand = "high">>
<<elseif _trust >= 30>>
<<set $jobState.workTrustBand = "mid">>
<<else>>
<<set $jobState.workTrustBand = "low">>
<</if>>
<</widget>>
/* Delta is signed (+/-). Clamps to 0-100 and updates trust band. */
<<widget "jobAdjustTrust">>
<<jobEnsureState>>
<<set _delta = parseInt($args[0], 10) || 0>>
<<set _current = parseInt($jobState.workTrustScore, 10) || 0>>
<<set _next = Math.max(0, Math.min(100, _current + _delta))>>
<<set $jobState.workTrustScore = _next>>
<<jobSetTrustBand>>
<</widget>>
<<widget "jobGetMeetingPassage">>
<<set _jid = ($job && $job.id) ? $job.id : null>>
<<set _j = (_jid && setup.jobs && setup.jobs[_jid]) ? setup.jobs[_jid] : null>>
<<if _j>>
<<set _return = _j.bossMeetingPassage || _j.bossOfficePassage || "jobBossOfficeCall_ruby">>
<<else>>
<<set _return = "jobBossOfficeCall_ruby">>
<</if>>
<</widget>>
/* Builds weekly pay record; deductions discount is applied after base deductions. */
<<widget "jobPrepareWeeklyPay">>
<<jobEnsureState>>
<<if !$job || !$job.id || !setup.jobs[$job.id]>><<return>><</if>>
<<set _job = setup.jobs[$job.id]>>
<<set _gross = Math.max(0, parseFloat($jobState.weeklyEarnings || 0))>>
<<set _dedBase = Math.max(0, parseFloat($jobState.weeklyDeductions || 0))>>
<<if _gross <= 0 && _dedBase <= 0>>
<<set $jobState.weeklyPayPending = null>>
<<return>>
<</if>>
<<set _trust = parseInt($jobState.workTrustScore, 10) || 0>>
<<set _discount = 0>>
<<set _thresholds = (_job.trustPolicy && _job.trustPolicy.discountThresholds) ? _job.trustPolicy.discountThresholds : []>>
<<for _t range _thresholds>>
<<if _trust >= (_t.trust || 0) && (_t.percent || 0) > _discount>>
<<set _discount = _t.percent>>
<</if>>
<</for>>
<<set _dedAfter = Math.round((_dedBase * (1 - (_discount / 100))) * 100) / 100>>
<<set _net = Math.max(0, Math.round((_gross - _dedAfter) * 100) / 100)>>
<<set _weekKey = $timeSys.year + "-" + $timeSys.month + "-" + $timeSys.day>>
<<set $jobState.weeklyPayPending = {
weekKey: _weekKey,
gross: _gross,
deductionsBase: _dedBase,
discountPercent: _discount,
deductionsFinal: _dedAfter,
net: _net,
jobId: $job.id,
preparedAtWeekday: $timeSys.weekday
}>>
<</widget>>
/* Pays current pending record and closes it. */
<<widget "jobFinalizeWeeklyPay">>
<<jobEnsureState>>
<<if !$jobState.weeklyPayPending>><<return>><</if>>
<<set _pay = $jobState.weeklyPayPending>>
<<set _net = Math.max(0, parseFloat(_pay.net || 0))>>
<<if _net > 0>>
<<set _notifyTxt to 'Weekly pay received: $' + _net.toFixed(2)>>
<<earnMoney _net _notifyTxt>>
<</if>>
<<set $jobState.weeklyPayPending = null>>
<</widget>>
<<widget "jobGetSkillValue">>
<<set _path = ($args[0] || "") + "">>
<<set _return = 0>>
<<if !_path>><<return>><</if>>
<<run
(function() {
var p = State.temporary._path || "";
var out = 0;
var node = State.variables;
var parts = p.split(".");
if (parts[0] === "skills") {
node = State.variables.skills || {};
parts = parts.slice(1);
} else {
node = State.variables.skills || {};
}
for (var i = 0; i < parts.length; i++) {
if (!node || typeof node !== "object") { node = null; break; }
node = node[parts[i]];
}
if (typeof node === "number") out = node;
State.temporary._return = out;
})();
>>
<<set _return = parseFloat(_return) || 0>>
<</widget>>
<<script>>
window.jobApplyPhoneExcuse = function (mode, lieType, charId) {
try {
var V = State.variables || {};
if (!V.job || !V.job.id || !window.setup || !setup.jobs || !setup.jobs[V.job.id]) return { ok: false, reason: "no_job" };
var job = setup.jobs[V.job.id];
if (!V.jobState || typeof V.jobState !== "object") return { ok: false, reason: "no_job_state" };
if (charId && job.bossCharId && charId !== job.bossCharId) return { ok: false, reason: "wrong_boss" };
var t = V.timeSys || {};
var dateKey = (Number(t.year) || 0) * 10000 + (Number(t.month) || 0) * 100 + (Number(t.day) || 0);
V.jobState.excuseForDateKey = dateKey;
V.jobState.excuseSentToday = true;
if (mode === "honest") {
V.jobState.excuseMode = "honest";
if (window.notifySuccess) window.notifySuccess("You informed your manager you can't make it today.");
return { ok: true, mode: "honest" };
}
if (mode !== "lie") return { ok: false, reason: "invalid_mode" };
var corruption = parseInt(V.corruption, 10) || 0;
if (corruption !== 2) {
V.jobState.excuseMode = "honest";
if (window.notifyWarning) window.notifyWarning("You couldn't bring yourself to lie, so you sent an honest message.");
return { ok: true, mode: "honest", forcedHonest: true };
}
var policy = (job.liePolicy && typeof job.liePolicy === "object") ? job.liePolicy : {};
var options = policy.options || {};
var base = Number(options[lieType]);
if (!Number.isFinite(base)) base = 50;
var conf = Math.max(0, Math.min(100, Number(V.confidence) || 0));
var bonusMax = Number(policy.confidenceBonusMax);
if (!Number.isFinite(bonusMax)) bonusMax = 20;
var bonus = (conf / 100) * bonusMax;
var minCap = Number(policy.successCapMin);
var maxCap = Number(policy.successCapMax);
if (!Number.isFinite(minCap)) minCap = 5;
if (!Number.isFinite(maxCap)) maxCap = 95;
var chance = Math.max(minCap, Math.min(maxCap, base + bonus));
var roll = Math.random() * 100;
var success = roll < chance;
V.jobState.excuseMode = success ? "lie_success" : "lie_fail";
if (window.notifyWarning) {
window.notifyWarning(success ? "Your excuse seems to have worked." : "Your excuse sounded suspicious.");
}
return { ok: true, mode: V.jobState.excuseMode, chance: chance, roll: roll };
} catch (e) {
return { ok: false, reason: "exception" };
}
};
/* In-person lie attempt at the boss meeting. Picks a random lie from the context-matching pool
(based on bossMeetingReason), rolls success using persuasion + problemSolving + confidence,
and applies outcomes:
- Success: partial trust refund by reason.
- Fail: adds daily-pay × failDailyPayPenaltyPct/100 to weeklyDeductions (reflected next envelope),
jobAdjustTrust(-failTrustExtraPenalty), flags termination if trust hits 0.
Always grants skill ticks on attempt. */
window.jobRollInPersonLie = function () {
try {
var V = State.variables || {};
if (!V.job || !V.job.id || !window.setup || !setup.jobs || !setup.jobs[V.job.id]) return { ok: false, reason: "no_job" };
var job = setup.jobs[V.job.id];
if (!V.jobState || typeof V.jobState !== "object") return { ok: false, reason: "no_job_state" };
var policy = (job.inPersonLiePolicy && typeof job.inPersonLiePolicy === "object") ? job.inPersonLiePolicy : null;
if (!policy) return { ok: false, reason: "no_policy" };
if (V.jobState.bossLieAttemptedThisMeeting) return { ok: false, reason: "already_attempted" };
var corrReq = Number(policy.requiresCorruption);
if (!Number.isFinite(corrReq)) corrReq = 2;
if ((parseInt(V.corruption, 10) || 0) < corrReq) return { ok: false, reason: "corruption_low" };
var confReq = Number(policy.requiresConfidence);
if (!Number.isFinite(confReq)) confReq = 10;
var conf = Math.max(0, Math.min(100, Number(V.confidence) || 0));
if (conf < confReq) return { ok: false, reason: "confidence_low" };
/* Map attendance reason to lie pool */
var reason = V.jobState.bossMeetingReason || "";
var poolKey = "partial";
if (reason === "no_show") poolKey = "noshow";
else if (reason === "missed_meeting") poolKey = "meeting";
else if (reason === "partial_4_8" || reason === "partial_6_8" || reason === "insufficient_day") poolKey = "partial";
var opts = policy.options || {};
var candidates = [];
Object.keys(opts).forEach(function (k) {
if (opts[k] && opts[k].pool === poolKey) candidates.push(k);
});
if (candidates.length === 0) {
/* Fallback to any lie if the pool is empty */
candidates = Object.keys(opts);
}
if (candidates.length === 0) return { ok: false, reason: "no_lies" };
var pickKey = candidates[Math.floor(Math.random() * candidates.length)];
var pick = opts[pickKey] || {};
var base = Number(pick.base);
if (!Number.isFinite(base)) base = 45;
/* Skill bonuses */
var skills = V.skills || {};
var persuasion = Math.max(0, Math.min(100, Number((skills.social || {}).persuasion) || 0));
var problemSolving = Math.max(0, Math.min(100, Number((skills.mental || {}).problemSolving) || 0));
var pMax = Number(policy.persuasionBonusMax); if (!Number.isFinite(pMax)) pMax = 25;
var psMax = Number(policy.problemSolvingBonusMax); if (!Number.isFinite(psMax)) psMax = 15;
var cMax = Number(policy.confidenceBonusMax); if (!Number.isFinite(cMax)) cMax = 10;
var bonus = (persuasion / 100) * pMax + (problemSolving / 100) * psMax + (conf / 100) * cMax;
var minCap = Number(policy.successCapMin); if (!Number.isFinite(minCap)) minCap = 5;
var maxCap = Number(policy.successCapMax); if (!Number.isFinite(maxCap)) maxCap = 92;
var chance = Math.max(minCap, Math.min(maxCap, base + bonus));
var roll = Math.random() * 100;
var success = roll < chance;
V.jobState.bossLieAttemptedThisMeeting = true;
V.jobState.bossLieLastKey = pickKey;
V.jobState.bossLieLastReason = reason;
V.jobState.bossLieLastChance = Math.round(chance * 10) / 10;
V.jobState.bossLieLastResult = success ? "success" : "fail";
if (success) {
var refundMap = policy.successTrustRefundByReason || {};
var refund = Number(refundMap[reason]);
if (!Number.isFinite(refund)) refund = 5;
if (refund > 0) {
var cur = parseInt(V.jobState.workTrustScore, 10) || 0;
V.jobState.workTrustScore = Math.max(0, Math.min(100, cur + refund));
/* Recompute band */
var ts = V.jobState.workTrustScore;
V.jobState.workTrustBand = ts >= 70 ? "high" : (ts >= 30 ? "mid" : "low");
}
} else {
/* Extra trust penalty */
var extra = Number(policy.failTrustExtraPenalty);
if (!Number.isFinite(extra)) extra = 8;
var curT = parseInt(V.jobState.workTrustScore, 10) || 0;
V.jobState.workTrustScore = Math.max(0, Math.min(100, curT - extra));
var tsF = V.jobState.workTrustScore;
V.jobState.workTrustBand = tsF >= 70 ? "high" : (tsF >= 30 ? "mid" : "low");
/* Daily-pay × pct → weeklyDeductions (broken-glass pipe, applied to NEXT envelope) */
var pct = Number(policy.failDailyPayPenaltyPct);
if (!Number.isFinite(pct)) pct = 50;
var tier = Math.max(1, parseInt((V.job || {}).tier, 10) || 1);
var wageByTier = Array.isArray(job.wageByTier) ? job.wageByTier : [];
var wage = Number(wageByTier[tier - 1]);
if (!Number.isFinite(wage) || wage <= 0) wage = Number(job.wagePerHour) || 3;
var dailyPay = wage * 8;
var penaltyAmount = Math.round(dailyPay * (pct / 100) * 100) / 100;
V.jobState.weeklyDeductions = (Number(V.jobState.weeklyDeductions) || 0) + penaltyAmount;
V.jobState.bossLieLastPenaltyAmount = penaltyAmount;
/* Termination check */
var dismissAt = (job.trustPolicy && job.trustPolicy.dismissalAtTrust != null) ? job.trustPolicy.dismissalAtTrust : 0;
if ((V.jobState.workTrustScore || 0) <= dismissAt) {
V.jobState.terminationPending = true;
V.jobState.bossMeetingReason = "termination_trust0";
}
}
return { ok: true, success: success, key: pickKey, chance: chance, roll: roll };
} catch (e) {
return { ok: false, reason: "exception" };
}
};
<</script>>
<<widget "jobCanAdvanceTier">>
<<set _return = false>>
<<if !$job || !$job.id || !setup.jobs[$job.id]>><<return>><</if>>
<<set _j = setup.jobs[$job.id]>>
<<set _tier = parseInt($job.tier, 10) || 1>>
<<set _next = _tier + 1>>
<<set _maxTier = _j.tierMax || 6>>
<<if _next > _maxTier>><<return>><</if>>
<<set _rules = _j.tierProgressionRules || {}>>
<<set _rule = _rules[_next] || null>>
<<if !_rule>>
<<set _reqs = _j.tierExperienceRequirements || []>>
<<set _needXp = _reqs[_tier] || 0>>
<<if ($jobState.jobExperience || 0) >= _needXp>><<set _return = true>><</if>>
<<return>>
<</if>>
<<set _ok = true>>
<<set _needXp = _rule.jobExperience || 0>>
<<if ($jobState.jobExperience || 0) < _needXp>><<set _ok = false>><</if>>
<<if _ok && _rule.stats>>
<<for _s, _need range _rule.stats>>
<<run State.temporary._jobTmpStat = (State.variables[_s] != null ? parseFloat(State.variables[_s]) : 0)>>
<<set _have = parseFloat(State.temporary._jobTmpStat) || 0>>
<<if _have < (_need || 0)>><<set _ok = false>><</if>>
<</for>>
<</if>>
<<if _ok && _rule.skills>>
<<for _spath, _sneed range _rule.skills>>
<<jobGetSkillValue _spath>>
<<if (_return || 0) < (_sneed || 0)>><<set _ok = false>><</if>>
<</for>>
<</if>>
<<set _return = _ok>>
<</widget>>
/* Set $jobAvailableShifts (filter by schedule + energy + needs). Block shift if tracked hunger/thirst/bladder >= 50. */
<<widget "jobSetAvailableShifts">>
<<set _jobId = $args[0] || $job.id>>
<<set _job = setup.jobs[_jobId]>>
<<set $jobWorkNeedsBlock = false>>
<<if !_job>>
<<set $jobAvailableShifts = []>>
<<else>>
<<set _needMax = 50>>
<<set _needsOk = true>>
<<if $gameSettings.trackHunger && (parseInt($hunger, 10) || 0) >= _needMax>>
<<set _needsOk = false>>
<</if>>
<<if $gameSettings.trackThirst && (parseInt($thirst, 10) || 0) >= _needMax>>
<<set _needsOk = false>>
<</if>>
<<if $gameSettings.trackBladder && (parseInt($bladder, 10) || 0) >= _needMax>>
<<set _needsOk = false>>
<</if>>
<<set _hour = parseInt($timeSys.hour, 10)>>
<<set _close = _job.schedule.close>>
<<set _energy = parseInt($energy, 10) || 0>>
<<set _hoursToday = $jobState.hoursToday || 0>>
<<set _requiredHoursPerDay = _job.requiredHoursPerDay != null ? _job.requiredHoursPerDay : 8>>
<<set _options = _job.shiftHoursOptions || [2, 4, 6, 8]>>
<<set $jobAvailableShifts = []>>
/* Off-day: no shifts available */
<<set _offDays = (_job.schedule && _job.schedule.offWeekdays) || []>>
<<if _offDays.includes($timeSys.weekday)>>
/* scheduled off leave $jobAvailableShifts empty */
<<elseif !_needsOk>>
<<set $jobWorkNeedsBlock = true>>
<<else>>
<<for _h range _options>>
<<set _mult = _h / 4>>
<<set _energyCost = Math.ceil(Math.abs((_job.statCostsPer4Hours || {}).energy || 50) * _mult)>>
/* Same rule as watch TV: need shift energy cost + 10 reserve to start */
<<set _energyNeed = _energyCost + 10>>
<<if _hour + _h <= _close && _energy >= _energyNeed && _hoursToday + _h <= _requiredHoursPerDay>>
<<run $jobAvailableShifts.push(_h)>>
<</if>>
<</for>>
<</if>>
<</if>>
<</widget>>
/* Execute a shift: advanceTime, apply stat/skill costs, update earnings.
Duration in MINUTES (e.g. 120, 240, 360, 480). advanceTime expects minutes. */
<<widget "jobExecuteShift">>
<<jobEnsureState>>
<<set $jobShiftSuccess = false>>
<<set _shiftMinutes = $args[0]>>
<<set _shiftHours = _shiftMinutes / 60>>
<<set _jobId = $job.id>>
<<if !_jobId>>
<<run window.notifyWarning("You don't have a job.")>>
<<return>>
<</if>>
<<set _job = setup.jobs[_jobId]>>
<<if !_job>>
<<return>>
<</if>>
/* Check work outfit (required slots must match job's requiredOutfit) */
<<if _job.shiftStart && _job.shiftStart.clothingCheck && _job.shiftStart.requiredOutfit>>
<<set _outfitOk = true>>
<<set _missing = []>>
<<for _slot, _requiredId range _job.shiftStart.requiredOutfit>>
<<set _worn = $wardrobe.equipped[_slot]>>
<<if _worn !== _requiredId>>
<<set _outfitOk = false>>
<<run _missing.push(_slot)>>
<</if>>
<</for>>
<<if !_outfitOk>>
<<run window.notifyWarning("You need to wear the work uniform (apron, top, and bottom from Ruby's Diner) to start a shift.")>>
<<return>>
<</if>>
<</if>>
/* Min energy: shift cost + 10 reserve (matches time picker / watch TV) */
<<set _mult = _shiftHours / 4>>
<<set _energyCost = Math.ceil(Math.abs((_job.statCostsPer4Hours || {}).energy || 50) * _mult)>>
<<set _energyNeed = _energyCost + 10>>
<<if ($energy || 0) < _energyNeed>>
<<run window.notifyWarning("Not enough energy. You need at least " + _energyNeed + " to start this shift.")>>
<<return>>
<</if>>
<<set _needMaxExec = 50>>
<<if $gameSettings.trackHunger && (parseInt($hunger, 10) || 0) >= _needMaxExec>>
<<run window.notifyWarning("You're too hungry to start a shift. Eat something first.")>>
<<return>>
<</if>>
<<if $gameSettings.trackThirst && (parseInt($thirst, 10) || 0) >= _needMaxExec>>
<<run window.notifyWarning("You're too thirsty to start a shift. Drink something first.")>>
<<return>>
<</if>>
<<if $gameSettings.trackBladder && (parseInt($bladder, 10) || 0) >= _needMaxExec>>
<<run window.notifyWarning("You need a bathroom break before you can start a shift.")>>
<<return>>
<</if>>
/* Check requiredHoursPerDay - block when daily quota reached */
<<set _requiredHoursPerDay = _job.requiredHoursPerDay != null ? _job.requiredHoursPerDay : 8>>
<<if ndef $jobState.hoursToday>><<set $jobState.hoursToday = 0>><</if>>
<<if $jobState.hoursToday >= _requiredHoursPerDay>>
<<run window.notifyWarning("You've already worked " + _requiredHoursPerDay + " hours today.")>>
<<return>>
<</if>>
/* Scheduled off day (e.g. Tue/Thu) */
<<set _offList = _job.schedule.offWeekdays || []>>
<<if _offList.includes($timeSys.weekday)>>
<<run window.notifyWarning("You don't have a shift scheduled today (day off).")>>
<<return>>
<</if>>
/* Check schedule (open <= currentHour, currentHour + shiftHours <= close) */
<<set _hour = $timeSys.hour>>
<<if _hour < _job.schedule.open>>
<<run window.notifyWarning("Workplace opens at " + _job.schedule.open + ":00.")>>
<<return>>
<</if>>
<<if _hour + _shiftHours > _job.schedule.close>>
<<run window.notifyWarning("Shift would end after closing time.")>>
<<return>>
<</if>>
/* Validate-only: pass 2nd arg "validate" to check without applying costs/time/pay */
<<if $args[1] === "validate">>
<<set $jobShiftSuccess = true>>
<<else>>
/* Apply stat costs (per 4h, scaled) */
<<set _mult = _shiftHours / 4>>
<<set _costs = _job.statCostsPer4Hours>>
<<if _costs.energy>>
<<loseStat "energy" Math.abs(_costs.energy * _mult)>>
<</if>>
<<if _costs.mood>>
<<loseStat "mood" Math.abs(_costs.mood * _mult)>>
<</if>>
<<if _costs.stress>>
<<set $stress = Math.min(100, ($stress || 0) + Math.round(_costs.stress * _mult))>>
<</if>>
<<if _costs.hygiene>>
<<loseStat "hygiene" Math.abs(_costs.hygiene * _mult)>>
<</if>>
/* Advance time */
<<advanceTime _shiftMinutes>>
<<set $jobState.lastShiftEndHour = $timeSys.hour>>
/* Skill gains (per 4h, scaled) */
<<set _gains = _job.skillGainsPer4Hours || []>>
<<for _g range _gains>>
<<set _baseAmt = (_g.amount || 0) * _mult>>
<<set _softMult = 1>>
<<set _soft = (_job.skillGainSoftCap && _job.skillGainSoftCap.bands) ? _job.skillGainSoftCap.bands : []>>
<<set _haveSkill = 0>>
<<if _soft.length && _g.category && _g.skill>>
<<if $skills && $skills[_g.category] && $skills[_g.category][_g.skill] != null>>
<<set _haveSkill = parseFloat($skills[_g.category][_g.skill]) || 0>>
<</if>>
<<for _band range _soft>>
<<if _haveSkill >= (_band.min || 0) && (_band.mult || 0) > 0>>
<<set _softMult = _band.mult>>
<</if>>
<</for>>
<</if>>
<<set _amt = Math.floor(_baseAmt * _softMult)>>
/* Optional floor for short shifts only at lower skill bands. */
<<set _shortMinH = (_job.skillGainSoftCap && _job.skillGainSoftCap.shortShiftMinGainHours != null) ? _job.skillGainSoftCap.shortShiftMinGainHours : 2>>
<<set _shortMinAmt = (_job.skillGainSoftCap && _job.skillGainSoftCap.shortShiftMinGainAmount != null) ? _job.skillGainSoftCap.shortShiftMinGainAmount : 1>>
<<set _shortMaxSkill = (_job.skillGainSoftCap && _job.skillGainSoftCap.shortShiftMinGainMaxSkill != null) ? _job.skillGainSoftCap.shortShiftMinGainMaxSkill : 50>>
<<if _baseAmt > 0 && _shiftHours <= _shortMinH && _amt < _shortMinAmt && _haveSkill < _shortMaxSkill>>
<<set _amt = _shortMinAmt>>
<</if>>
<<if _amt > 0>>
<<gainSkill _g.category _g.skill _amt>>
<</if>>
<</for>>
/* Job experience
Keep XP capped at the current tier gate until promotion is approved,
so progress does not spill into the next tier bar early. */
<<set _xp = Math.round((_job.xpPerHour || 5) * _shiftHours)>>
<<set $jobState.jobExperience += _xp>>
<<set _xpReqs = _job.tierExperienceRequirements || []>>
<<set _tierNow = parseInt($job.tier, 10) || 1>>
<<set _tierXpCap = _xpReqs[_tierNow] || 0>>
<<if _tierXpCap > 0 && ($jobState.jobExperience || 0) > _tierXpCap>>
<<set $jobState.jobExperience = _tierXpCap>>
<</if>>
/* Earnings (per shift, current tier wage) */
<<set _tier = $job.tier || 1>>
<<set _wage = (_job.wageByTier && _job.wageByTier[_tier - 1]) ? _job.wageByTier[_tier - 1] : _job.wagePerHour || 3>>
<<set _earned = Math.round(_wage * _shiftHours * 100) / 100>>
<<set $jobState.weeklyEarnings += _earned>>
<<set $jobState.hoursThisWeek += _shiftHours>>
<<if ndef $jobState.hoursToday>><<set $jobState.hoursToday = 0>><</if>>
<<set _hoursTodayBefore = $jobState.hoursToday || 0>>
<<set $jobState.hoursToday += _shiftHours>>
<<set _requiredHoursPerDay = _job.requiredHoursPerDay != null ? _job.requiredHoursPerDay : 8>>
<<set _fullDayGain = Math.abs((_job.trustPolicy && _job.trustPolicy.fullDayGain != null) ? _job.trustPolicy.fullDayGain : 4)>>
<<if _hoursTodayBefore < _requiredHoursPerDay && ($jobState.hoursToday || 0) >= _requiredHoursPerDay && _fullDayGain > 0>>
<<jobAdjustTrust _fullDayGain>>
<</if>>
/* Only count 1 worked day per calendar day, not per shift */
<<if !$jobState.workedToday>>
<<set $jobState.totalDaysWorked += 1>>
<</if>>
<<set $jobState.shiftsToday += 1>>
<<set $jobState.workedToday = true>>
<<set $jobShiftSuccess = true>>
<<run window.notifySuccess("Shift done. +$" + _earned.toFixed(2) + " added to this week's payroll.")>>
<</if>>
<</widget>>
/* Process pay day: finalize weekly payroll record and reset counters.
Actual payment happens on next work start event (jobWeeklyPayEnvelope). */
<<widget "jobProcessPayDay">>
<<jobEnsureState>>
<<if !$job || !$job.id>>
<<return>>
<</if>>
<<set _job = (setup.jobs && setup.jobs[$job.id]) ? setup.jobs[$job.id] : null>>
<<if !_job>>
/* Save may contain an old/removed job id; fail safe instead of crashing. */
<<set $job.id = null>>
<<set $job.tier = 1>>
<<set $job.startedOn = null>>
<<jobResetState>>
<<return>>
<</if>>
/* Avoid double pay (same day) */
<<set _payDateKey = $timeSys.year + "-" + $timeSys.month + "-" + $timeSys.day>>
<<if $jobState.lastPayWeek === _payDateKey>>
<<return>>
<</if>>
/* Daily insufficiency means mandatory manager talk before next shift. */
<<set _insufficientDays = $jobState.insufficientDaysThisWeek || 0>>
<<if _insufficientDays > 0>>
<<set $jobState.bossMeetingRequired = true>>
<<if !$jobState.bossMeetingReason>><<set $jobState.bossMeetingReason = "insufficient_day">><</if>>
<<set $jobState.bossWantsToSeePlayer = true>>
<<set $jobState.visitedBossThisMonday = false>>
<<else>>
<<set $jobState.bossWantsToSeePlayer = false>>
<</if>>
/* Prepare single weekly pay record (no stacking); paid later at work entry scene. */
<<jobPrepareWeeklyPay>>
/* Reset for new week */
<<set $jobState.hoursThisWeek = 0>>
<<set $jobState.weeklyEarnings = 0>>
<<set $jobState.weeklyDeductions = 0>>
<<set $jobState.lastPayWeek = _payDateKey>>
<<set $jobState.insufficientDaysThisWeek = 0>>
<<set $jobState.excuseMode = "">>
<<set $jobState.excuseSentToday = false>>
<</widget>>
/* Record insufficient days before reset. Call before jobResetDaily in advanceDay.
Args: days, oldWeekday, leaveYear, leaveMonth, leaveDay leave* = calendar day being closed (before advanceDay's date +=).
Only counts days on or after $job.startedOn (set to "first work day" when accepting Ruby's job). */
<<widget "jobRecordInsufficientDay">>
<<jobEnsureState>>
<<if ndef $jobState>>
<<return>>
<</if>>
<<if !$jobState.firstWorkDayEventShown>>
<<return>>
<</if>>
<<set _days = $args[0] || 1>>
<<set _oldWeekday = $args[1]>>
<<set _ly = $args[2]>>
<<set _lm = $args[3]>>
<<set _ld = $args[4]>>
<<if !$job || !$job.id>>
<<return>>
<</if>>
<<set _job = setup.jobs != null ? setup.jobs[$job.id] : null>>
<<if _job && _job.schedule>>
/* Insufficient = scheduled work day where player did NOT complete requiredHoursPerDay.
Trust system classifies: no-show / partial_4_8 / partial_6_8. */
<<if ndef $jobState.insufficientDaysThisWeek>><<set $jobState.insufficientDaysThisWeek = 0>><</if>>
<<set _off = _job.schedule.offWeekdays || []>>
<<set _reqH = _job.requiredHoursPerDay != null ? _job.requiredHoursPerDay : 8>>
<<set _todayH = $jobState.hoursToday || 0>>
<<set _policy = _job.trustPolicy || {}>>
<<set _delta = 0>>
<<set _reason = "">>
<<set _startKey = 0>>
<<if $job.startedOn && $job.startedOn.year>>
<<set _startKey = $job.startedOn.year * 10000 + $job.startedOn.month * 100 + $job.startedOn.day>>
<</if>>
<<if $args.length >= 5>>
<<set _leaveKey = _ly * 10000 + _lm * 100 + _ld>>
<<else>>
<<set _leaveKey = $timeSys.year * 10000 + $timeSys.month * 100 + $timeSys.day>>
<</if>>
<<set _onOrAfterStart = (_startKey <= 0 || _leaveKey >= _startKey)>>
<<set _addFirst = false>>
<<if _onOrAfterStart>>
<<if _oldWeekday == null>>
<<set _addFirst = _todayH < _reqH>>
<<else>>
<<set _firstOff = _off.includes(_oldWeekday)>>
<<set _addFirst = !_firstOff && _todayH < _reqH>>
<</if>>
<</if>>
<<if _addFirst>>
<<set $jobState.insufficientDaysThisWeek = ($jobState.insufficientDaysThisWeek || 0) + 1>>
<<if _todayH <= 0>>
<<set _reason = "no_show">>
<<set _excuseMode = "none">>
<<if ndef $jobState.excuseForDateKey>><<set $jobState.excuseForDateKey = 0>><</if>>
<<if ndef $jobState.excuseMode>><<set $jobState.excuseMode = "">><</if>>
<<if $jobState.excuseForDateKey === _leaveKey && $jobState.excuseMode>>
<<set _excuseMode = $jobState.excuseMode>>
<</if>>
<<set _msgPenalty = (_policy.noShowMessagePenalty || {})>>
<<if _excuseMode === "honest">>
<<set _delta = -Math.abs(_msgPenalty.honest != null ? _msgPenalty.honest : 10)>>
<<elseif _excuseMode === "lie_success">>
<<set _delta = -Math.abs(_msgPenalty.lieSuccess != null ? _msgPenalty.lieSuccess : 8)>>
<<elseif _excuseMode === "lie_fail">>
<<set _delta = -Math.abs(_msgPenalty.lieFail != null ? _msgPenalty.lieFail : 16)>>
<<else>>
<<set _delta = -Math.abs(_msgPenalty.none != null ? _msgPenalty.none : (_policy.noShowPenalty != null ? _policy.noShowPenalty : 20))>>
<</if>>
<<elseif _todayH <= 4>>
<<set _reason = "partial_4_8">>
<<set _delta = -Math.abs(_policy.partial4Penalty != null ? _policy.partial4Penalty : 10)>>
<<else>>
<<set _reason = "partial_6_8">>
<<set _delta = -Math.abs(_policy.partial6Penalty != null ? _policy.partial6Penalty : 6)>>
<</if>>
<<jobAdjustTrust _delta>>
<<set $jobState.lastInsufficientType = _reason>>
<<set $jobState.lastInsufficientHours = _todayH>>
<<set $jobState.bossMeetingRequired = true>>
<<set $jobState.bossMeetingReason = _reason>>
<<set $jobState.bossWantsToSeePlayer = true>>
<<set $jobState.visitedBossThisMonday = false>>
<<set _dismissAt = (_policy.dismissalAtTrust != null) ? _policy.dismissalAtTrust : 0>>
<<if ($jobState.workTrustScore || 0) <= _dismissAt>>
<<set $jobState.terminationPending = true>>
<<set $jobState.bossMeetingReason = "termination_trust0">>
<</if>>
<<set $jobState.excuseMode = "">>
<</if>>
<<if _days > 1 && $args.length >= 5 && setup.jobDateAddDays>>
<<for _i = 1; _i < _days; _i++>>
<<set _dinfo = setup.jobDateAddDays(_ly, _lm, _ld, _i)>>
<<set _midKey = _dinfo.year * 10000 + _dinfo.month * 100 + _dinfo.day>>
<<if (_startKey <= 0 || _midKey >= _startKey) && !_off.includes(_dinfo.weekday)>>
<<set $jobState.insufficientDaysThisWeek = ($jobState.insufficientDaysThisWeek || 0) + 1>>
<</if>>
<</for>>
<</if>>
<</if>>
<</widget>>
/* Check if player skipped Monday boss call. If you advance *past* a playable Monday without visiting, fire.
weekday 1 = Monday. Uses calendar steps: (_oldWeekday + k) % 7 for k = 1.._days is each new wake-up day. */
<<widget "jobCheckSkippedBossCall">>
<<jobEnsureState>>
<<set _days = $args[0] || 1>>
<<set _oldWeekday = $args[1] || 0>>
/* ALL guards in a single <<if>> only run body when boss actually wants to see player */
<<if $job && $job.id && $jobState && $jobState.bossWantsToSeePlayer && !$jobState.visitedBossThisMonday>>
<<set _job = (setup.jobs && setup.jobs[$job.id]) ? setup.jobs[$job.id] : null>>
<<if _job>>
<<set _off = (_job.schedule && _job.schedule.offWeekdays) || []>>
<<set _mondayIsScheduledOff = Array.isArray(_off) && _off.includes(1)>>
/* Left Monday while still owing a visit (wake up Tuesday+ from a Monday start) */
<<set _missedBossDeadline = (_oldWeekday === 1 && _days >= 1 && !_mondayIsScheduledOff)>>
/* Slept through at least one full calendar Monday */
<<if !_missedBossDeadline && _days > 1>>
<<for _k = 1; _k < _days; _k++>>
<<set _w = (_oldWeekday + _k) % 7>>
<<if _w === 1 && !_mondayIsScheduledOff>>
<<set _missedBossDeadline = true>>
<<break>>
<</if>>
<</for>>
<</if>>
<<if _missedBossDeadline>>
<<set $jobState.bossMeetingRequired = true>>
<<set $jobState.bossMeetingReason = "missed_meeting">>
<<set $jobState.bossWantsToSeePlayer = true>>
<<jobAdjustTrust -10>>
<<if ($jobState.workTrustScore || 0) <= 0>>
<<set $jobState.terminationPending = true>>
<<set $jobState.bossMeetingReason = "termination_trust0">>
<</if>>
<</if>>
<</if>>
<</if>>
<</widget>>
/* Reset entire job state. Call when fired or when starting a new job. */
<<widget "jobResetState">>
<<set $jobState = {
hoursThisWeek: 0,
hoursToday: 0,
shiftsToday: 0,
totalDaysWorked: 0,
jobExperience: 0,
weeklyEarnings: 0,
weeklyDeductions: 0,
lastPayWeek: null,
lastShiftEndHour: null,
minHoursWarningCount: 0,
firstWorkDayEventShown: false,
insufficientDaysThisWeek: 0,
bossWantsToSeePlayer: false,
visitedBossThisMonday: false,
freeMealUsedToday: false,
workedToday: false,
workTrustScore: 60,
workTrustBand: "mid",
bossMeetingRequired: false,
bossMeetingReason: "",
terminationPending: false,
promotionPending: false,
pendingTier: 0,
weeklyPayPending: null,
lastInsufficientType: "",
lastInsufficientHours: 0,
excuseForDateKey: 0,
excuseMode: "",
excuseSentToday: false,
bossLieAttemptedThisMeeting: false,
bossLieLastResult: "",
bossLieLastReason: "",
bossLieLastKey: "",
bossLieLastChance: 0,
bossLieLastPenaltyAmount: 0
}>>
<</widget>>
/* Reset daily job state (shiftsToday, hoursToday, freeMealUsedToday, lastShiftEndHour). Call at start of advanceDay. */
<<widget "jobResetDaily">>
<<if def $jobState>>
<<set $jobState.shiftsToday = 0>>
<<set $jobState.hoursToday = 0>>
<<set $jobState.workedToday = false>>
<<set $jobState.freeMealUsedToday = false>>
<<set $jobState.lastShiftEndHour = null>>
<<set $jobState.excuseSentToday = false>>
<</if>>
<</widget>>
/* Work button for a workplace: locked (daily complete, already worked, outfit, energy) or link to jobWorkSelect.
Before schedule.open on a work day: "Wait for shift" → jobWaitForShift advances time to opening; then "Work" applies.
Usage: <<jobWorkButton "dinerRubys">> or <<jobWorkButton>> (uses $location as workplace) */
<<widget "jobWorkButton">>
<<jobEnsureState>>
<<set _workplace = $args[0] || $location>>
<<if _workplace && $job && $job.id && setup.jobs[$job.id] && setup.jobs[$job.id].workplace === _workplace>>
<<set _job = setup.jobs[$job.id]>>
<<set _hour = $timeSys.hour>>
<<set _currentDate = $timeSys.year * 10000 + $timeSys.month * 100 + $timeSys.day>>
<<set _startDate = ($job.startedOn != null) ? ($job.startedOn.year * 10000 + $job.startedOn.month * 100 + $job.startedOn.day) : 0>>
<<set _offDays = (_job.schedule && _job.schedule.offWeekdays) || []>>
<<set _isScheduleOffDay = _offDays.includes($timeSys.weekday)>>
<<set _jobStartedDate = _startDate > 0 && _currentDate >= _startDate>>
<<set _openH = _job.schedule.open>>
<<set _closeH = _job.schedule.close>>
<<set _nowMins = (_hour * 60) + ($timeSys.minute || 0)>>
<<set _openMins = _openH * 60>>
<<set _beforeShiftOpens = _jobStartedDate && !_isScheduleOffDay && _nowMins < _openMins && _hour < _closeH>>
<<set _shiftHoursOpen = _jobStartedDate && !_isScheduleOffDay && _nowMins >= _openMins && _hour < _closeH>>
<<if _jobStartedDate && _isScheduleOffDay && _hour < _closeH>>
<<set _offNames = []>>
<<for _wd range _offDays>>
<<run _offNames.push($timeConfig.weekdayNames[_wd])>>
<</for>>
<<set _offHint = _offNames.length ? _offNames.join(" & ") : "scheduled off">>
<<set _dayOffLockText = "Day off (" + _offHint + ")">>
/* On off days: if weekly pay is pending, collect it first; then boss summons. */
<<set _payPendingOffDay = !!$jobState.weeklyPayPending>>
<<set _payPassageOffDay = _job.paydayScenePassage || "jobWeeklyPayEnvelope">>
<<set _bossSummonOffDay = (($jobState.bossWantsToSeePlayer) || ($jobState.bossMeetingRequired) || ($jobState.terminationPending) || ($jobState.promotionPending)) && (_job.bossMeetingPassage || _job.bossOfficePassage)>>
<<if _payPendingOffDay>>
<<btn "Collect weekly pay" _payPassageOffDay>><</btn>>
<<elseif _bossSummonOffDay>>
<<set _bossFirst = setup.characterDefs[_job.bossCharId].firstName>>
<<set _btnSeeBoss = "See " + _bossFirst>>
<<jobGetMeetingPassage>>
<<btn _btnSeeBoss _return>><</btn>>
<<else>>
<<btn "Work" "" "locked" _dayOffLockText>><</btn>>
<</if>>
<<elseif _beforeShiftOpens>>
<<btn "Wait for shift" "jobWaitForShift">><<set $jobWaitReturnTo = $location>><</btn>>
<<elseif _shiftHoursOpen>>
<<silently>>
<<set _workLocked = false>>
<<set _workLockedReasons = []>>
<<set _requiredHoursPerDay = _job.requiredHoursPerDay != null ? _job.requiredHoursPerDay : 8>>
<<set _hoursToday = $jobState.hoursToday || 0>>
<<if _hoursToday >= _requiredHoursPerDay>>
<<set _workLocked = true>>
<<set _workLockedText = "Already worked today (" + _hoursToday + "/" + _requiredHoursPerDay + " h)">>
<</if>>
<<if ndef $jobState.firstWorkDayEventShown>><<set $jobState.firstWorkDayEventShown = false>><</if>>
<<if !_workLocked && $jobState.firstWorkDayEventShown && _job.shiftStart && _job.shiftStart.clothingCheck && _job.shiftStart.requiredOutfit>>
<<for _slot, _requiredId range _job.shiftStart.requiredOutfit>>
<<set _worn = $wardrobe.equipped[_slot]>>
<<if _worn !== _requiredId>>
<<set _workLocked = true>>
<<run _workLockedReasons.push(_slot === "top" ? "work top" : _slot === "bottom" ? "work bottom" : "apron")>>
<</if>>
<</for>>
<</if>>
<<if !_workLocked>>
<<set _minEnergy2h = Math.ceil(Math.abs((_job.statCostsPer4Hours || {}).energy || 50) * (2/4))>>
<<set _minEnergy2hNeed = _minEnergy2h + 10>>
<<if ($energy || 0) < _minEnergy2hNeed>>
<<set _workLocked = true>>
<<run _workLockedReasons.push(_minEnergy2hNeed + " energy for 2h shift")>>
<</if>>
<</if>>
<<if _workLockedReasons.length>><<set _workLockedText = "Need: " + _workLockedReasons.join(", ")>><</if>>
<<set _payPending = !!$jobState.weeklyPayPending>>
<<set _payPassage = _job.paydayScenePassage || "jobWeeklyPayEnvelope">>
<<set _needsBoss = ($jobState.bossMeetingRequired || $jobState.bossWantsToSeePlayer || $jobState.terminationPending || $jobState.promotionPending)>>
<<jobGetMeetingPassage>>
<<set _meetingPassage = _return>>
<<if _payPending>>
<<set _workTarget = _payPassage>>
<<elseif _needsBoss>>
<<set _workTarget = _meetingPassage>>
<<else>>
<<set _workTarget = ($jobState.firstWorkDayEventShown ? "jobWorkSelect" : (_job.firstWorkDayEventPassage || "jobWorkSelect"))>>
<</if>>
<</silently>>
<<if _workLocked>>
<<btn "Work" "" "locked" _workLockedText>><</btn>>
<<else>>
<<btn "Work" _workTarget>><</btn>>
<</if>>
<</if>>
<</if>>
<</widget>>
<</nobr>>
<<nobr>>
<<set setup.lilyJogGossip = [
{
id: "maplewood_blonde_neighbor",
text: `<<narrative "Sunset Park">>You jog side by side along the wide trail. Leaves rustle softly overhead.<</narrative>>
<<dialog "parkRunnerLily">>You know that blonde down our street? Lives alone.<</dialog>>
<<dialog "player">>Which one?<</dialog>>
<<dialog "parkRunnerLily">>A different car parks at her door every night. My neighbor's literally counting plates now.<</dialog>>
<<dialog "player">>Seriously?<</dialog>>
<<dialog "parkRunnerLily">>Three different plates last week, she said.<</dialog>>
<<dialog "player">>Maybe they're friends?<</dialog>>
<<dialog "parkRunnerLily">>Friends don't show up at three in the morning, sweetie.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
id: "maplewood_rich_neighbor",
text: `<<narrative "Sunset Park">>You run under the shade of the trees. Lily glances at you sideways.<</narrative>>
<<dialog "parkRunnerLily">>Have you seen the big villa on the corner?<</dialog>>
<<dialog "player">>Which one?<</dialog>>
<<dialog "parkRunnerLily">>Some insanely rich, gorgeous guy lives there alone. Comes out of the shower and forgets to close the curtains sometimes.<</dialog>>
<<dialog "player">>What, he walks around naked?<</dialog>>
<<dialog "parkRunnerLily">>Sometimes not even a towel. The whole street watches.<</dialog>>
<<dialog "player">>That's insane.<</dialog>>
<<dialog "parkRunnerLily">>If that towel ever drops, traffic will lock up out here.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 1
},
{
id: "downtown_pulse_club",
text: `<<narrative "Sunset Park">>After running quietly for a while, Lily catches her breath and starts talking.<</narrative>>
<<dialog "player">>What are the clubs in Downtown actually like?<</dialog>>
<<dialog "parkRunnerLily">>Pulse is the busiest. Friday night you're stuck at the door for hours.<</dialog>>
<<dialog "player">>That packed?<</dialog>>
<<dialog "parkRunnerLily">>Packed. Inside it's rich types, models, money everywhere.<</dialog>>
<<dialog "player">>You ever been?<</dialog>>
<<dialog "parkRunnerLily">>A few times. Would you go to a place like that?<</dialog>>
<<dialog "player">>Not brave enough yet.<</dialog>>
<<dialog "parkRunnerLily">>Maybe one day we go together. What do you say?<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
id: "hillcrest_rich_wives",
text: `<<narrative "Sunset Park">>The path tilts upward and both of you start breathing harder.<</narrative>>
<<dialog "parkRunnerLily">>Those rich wives up in Hillcrest...<</dialog>>
<<dialog "player">>What about them?<</dialog>>
<<dialog "parkRunnerLily">>Daytime they play perfect wife and mother.<</dialog>>
<<dialog "parkRunnerLily">>At night they're rubbing on younger guys at the rooftop bars.<</dialog>>
<<dialog "player">>You serious?<</dialog>>
<<dialog "parkRunnerLily">>Saw one last month. Hand on the kid's waist, ring still on her finger.<</dialog>>
<<dialog "player">>While her husband's home?<</dialog>>
<<dialog "parkRunnerLily">>Yep. Money doesn't make them bold. Boredom does.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 1
},
{
id: "maplewood_gossip_culture",
text: `<<narrative "Sunset Park">>You reach a quieter corner of the park. Just your footsteps now.<</narrative>>
<<dialog "parkRunnerLily">>Everyone in Maplewood is in everyone else's business, I swear.<</dialog>>
<<dialog "player">>Really?<</dialog>>
<<dialog "parkRunnerLily">>Throw a backyard party Saturday and by Sunday the whole street knows what you drank and who you talked to.<</dialog>>
<<dialog "player">>Doesn't that bug you?<</dialog>>
<<dialog "parkRunnerLily">>It does. But also, kind of fun.<</dialog>>
<<dialog "player">>So you keep tabs on the neighbors too?<</dialog>>
<<dialog "parkRunnerLily">>Me? No... I just listen.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
id: "university_campus_nightlife",
text: `<<narrative "Sunset Park">>Lily holds the tempo, smiling a little.<</narrative>>
<<dialog "player">>What's the University District like?<</dialog>>
<<dialog "parkRunnerLily">>Young energy everywhere. Bars, parties, exam stress, all mixed together.<</dialog>>
<<dialog "player">>Really that intense?<</dialog>>
<<dialog "parkRunnerLily">>Very. Some girls head to other neighborhoods on weekends, they say.<</dialog>>
<<dialog "player">>Why?<</dialog>>
<<dialog "parkRunnerLily">>Rent's expensive, allowance doesn't cut it. You do the math.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
id: "southside_gang_territory",
text: `<<narrative "Sunset Park">>Lily's voice turns a bit more serious.<</narrative>>
<<dialog "parkRunnerLily">>Don't ever go into Southside alone at night.<</dialog>>
<<dialog "player">>That bad?<</dialog>>
<<dialog "parkRunnerLily">>Gangs have the blocks divided up, they say.<</dialog>>
<<dialog "player">>How do people live there?<</dialog>>
<<dialog "parkRunnerLily">>Quietly. They've learned not to stick their nose in things.<</dialog>>
<<dialog "player">>What about the police?<</dialog>>
<<dialog "parkRunnerLily">>They're there, but even they walk careful.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
id: "southside_wrong_color",
text: `<<narrative "Sunset Park">>A light breeze blows through the trees. Lily wipes sweat from her forehead.<</narrative>>
<<dialog "player">>Heard anything new about Southside?<</dialog>>
<<dialog "parkRunnerLily">>Last month they beat a guy for wearing the wrong color shoes.<</dialog>>
<<dialog "player">>That's crazy.<</dialog>>
<<dialog "parkRunnerLily">>Colors are signals on those streets.<</dialog>>
<<dialog "player">>Seriously?<</dialog>>
<<dialog "parkRunnerLily">>Wear the wrong one and you're sending a message without realizing it.<</dialog>>
<<dialog "parkRunnerLily">>A girl at the gym told me. Her sister grew up there.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
id: "suburbs_calm_facade",
text: `<<narrative "Sunset Park">>You jog along the flat path in the middle of the park. Lily shakes her head.<</narrative>>
<<dialog "parkRunnerLily">>Suburbs has been coming up a lot lately. Strange place.<</dialog>>
<<dialog "player">>What's happening there?<</dialog>>
<<dialog "parkRunnerLily">>From outside it looks like the perfect quiet family neighborhood.<</dialog>>
<<dialog "parkRunnerLily">>But apparently the drug trade runs pretty quietly out there.<</dialog>>
<<dialog "player">>Don't the police know?<</dialog>>
<<dialog "parkRunnerLily">>I'm sure they do. But word travels fast before anything happens.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
id: "suburbs_fake_quiet",
text: `<<narrative "Sunset Park">>Another jogger passes by. Lily lowers her voice slightly.<</narrative>>
<<dialog "player">>Is Suburbs really that bad?<</dialog>>
<<dialog "parkRunnerLily">>Not super dangerous. More like fake calm.<</dialog>>
<<dialog "player">>What do you mean?<</dialog>>
<<dialog "parkRunnerLily">>Streets are quiet, kids riding bikes during the day.<</dialog>>
<<dialog "player">>And at night?<</dialog>>
<<dialog "parkRunnerLily">>Same houses get customers coming and going. Pills, powder... whatever pays.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
id: "redlight_open_street",
text: `<<narrative "Sunset Park">>Lily lowers her voice as you continue along the trail.<</narrative>>
<<dialog "player">>What do you hear about Red Light Center?<</dialog>>
<<dialog "parkRunnerLily">>The girls on the street don't bother being subtle anymore.<</dialog>>
<<dialog "parkRunnerLily">>Some of them work practically in their underwear.<</dialog>>
<<dialog "player">>For real?<</dialog>>
<<dialog "parkRunnerLily">>A friend saw a girl drop a guy to his knees right on the sidewalk and go down on him in front of everyone.<</dialog>>
<<dialog "player">>What do the cops do?<</dialog>>
<<dialog "parkRunnerLily">>That area pretty much makes its own rules.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 1
},
{
id: "redlight_private_clubs",
text: `<<narrative "Sunset Park">>The late afternoon sun filters through the trees as you round a bend.<</narrative>>
<<dialog "player">>What are the clubs in Red Light like?<</dialog>>
<<dialog "parkRunnerLily">>There are strip clubs, and then the private entertainment houses.<</dialog>>
<<dialog "player">>What happens in the private ones?<</dialog>>
<<dialog "parkRunnerLily">>VIP rooms. You go in for a massage... and it ends differently.<</dialog>>
<<dialog "player">>You ever go inside?<</dialog>>
<<dialog "parkRunnerLily">>Only the lobby once. I figured out what was going on before I went any further.<</dialog>>
<<dialog "player">>Then what?<</dialog>>
<<dialog "parkRunnerLily">>I left. But if someone had asked me to stay... I might have thought about it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 2
},
{
id: "general_city_gossip",
text: `<<narrative "Sunset Park">>You both slow down slightly in the middle of the park. Lily looks around.<</narrative>>
<<dialog "parkRunnerLily">>Every neighborhood in this city feels like its own little world.<</dialog>>
<<dialog "player">>Which one do you think is the weirdest?<</dialog>>
<<dialog "parkRunnerLily">>Southside and the Suburbs for sure. One's too dangerous, the other's too fake.<</dialog>>
<<dialog "player">>What about Maplewood?<</dialog>>
<<dialog "parkRunnerLily">>Maplewood looks calm on the surface, but the gossip never stops.<</dialog>>
<<dialog "player">>Where would you actually want to live?<</dialog>>
<<dialog "parkRunnerLily">>Right here in Maplewood is fine for now. At least the running trail is nice.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
id: "general_maplewood_vs_others",
text: `<<narrative "Sunset Park">>Lily smiles as she looks at the trees around you.<</narrative>>
<<dialog "parkRunnerLily">>Maplewood is nice to live in, but I sometimes wonder about the other parts of the city.<</dialog>>
<<dialog "player">>Like which ones?<</dialog>>
<<dialog "parkRunnerLily">>Hillcrest is full of money. Downtown is nonstop energy. Red Light... that's a whole different story.<</dialog>>
<<dialog "player">>You curious about Red Light?<</dialog>>
<<dialog "parkRunnerLily">>A little. But everything about that place gets talked about way too openly.<</dialog>>
<<dialog "player">>What's your overall take on this city?<</dialog>>
<<dialog "parkRunnerLily">>It's beautiful... but you have to be careful. Every corner has its own secrets.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]>>
<</nobr>><<nobr>>
<<set setup.lilyTopics = setup.lilyTopics || {}>>
<<set setup.lilyTopics.common = setup.lilyTopics.common || {}>>
<<set setup.lilyTopics.common.level1 = {
"sunsetPark_common_morning": {
"cooldown_pond": [
{
text: `<<narrative "Sunset Park">>Lily is stretching her calves against a bench by the pond, still breathing hard from her run. Her breath fogs slightly in the early morning chill when she laughs.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_morning_cooldown_pond_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Morning! Nothing beats laps around the pond. Well… maybe coffee comes close.<</dialog>>
<<dialog "player">>You're making me feel lazy just watching you.<</dialog>>
<<dialog "parkRunnerLily">>Hey, you showed up. That already counts more than most people.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<narrative "Sunset Park">>Lily jogs lightly in place, then slows to a walk when she sees you and towels the sweat from her neck.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_morning_cooldown_pond_2.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Perfect timing. I was about to talk myself into one more lap. Be my excuse to stop?<</dialog>>
<<dialog "player">>I can be the excuse.<</dialog>>
<<dialog "parkRunnerLily">>Best kind of excuse. Recovery is part of the training, you know.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"mall_tip_followup": [
{
text: `<<narrative "Sunset Park">>Lily takes a long sip from her water bottle, then caps it with a satisfied click.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_morning_mall_tip_followup_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Hey, good to see you again! How did the sportswear hunt go? Did you check out the mall like I said?<</dialog>>
<<dialog "player">>Yeah, thanks for the tip.<</dialog>>
<<dialog "parkRunnerLily">>Anytime. Tell them your legs sent you they won’t get it, but it’ll be funny.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<narrative "Sunset Park">>Lily checks her watch, then relaxes and smiles.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_morning_mall_tip_followup_2.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>If you go for gear, hit the athletic floor first. Otherwise you’ll end up buying candles and socks you don’t need. Trust me, I’ve lived it.<</dialog>>
<<dialog "player">>That sounds very specific.<</dialog>>
<<dialog "parkRunnerLily">>I contain multitudes… mostly in the form of novelty socks.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"running_buddy": [
{
text: `<<narrative "Sunset Park">>Lily rolls her shoulders, eyes scanning the path like she’s already planning tomorrow’s route.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_morning_running_buddy_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>This park’s basically my second home now. If you ever want a running buddy, I’m usually here.<</dialog>>
<<dialog "player">>I might take you up on that… but slowly.<</dialog>>
<<dialog "parkRunnerLily">>Slow is perfect. Slow is how you actually stick with it without hating your life.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<narrative "Sunset Park">>A couple of ducks glide across the pond. Lily watches them with a small, amused smile.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_morning_running_buddy_2.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Fair warning though if you run with me, I will tell you extremely weird tree facts. It’s a coping mechanism.<</dialog>>
<<dialog "player">>How weird are we talking?<</dialog>>
<<dialog "parkRunnerLily">>Some maples can change their entire leaf color overnight if the temperature drops fast enough. Nature’s biggest drama queen.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
},
"sunsetPark_common_afternoon": {
"midday_heat": [
{
text: `<<narrative "Sunset Park">>Lily’s hair is up in a messy ponytail, a few strands stuck to her forehead. She waves at you from under the shade of a big oak.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_afternoon_midday_heat_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Afternoon runs are brutal. The sun doesn’t negotiate with anyone.<</dialog>>
<<dialog "player">>And yet you’re still out here.<</dialog>>
<<dialog "parkRunnerLily">>Stubbornness is a skill. That, and industrial amounts of sunscreen.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<narrative "Sunset Park">>Lily is on a cooldown walk, headphones around her neck, music low enough that you can hear the birds.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_afternoon_midday_heat_2.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>I used to power through midday heat like an idiot. Now I hydrate like a civilized human and actually live longer.<</dialog>>
<<dialog "player">>Very sensible.<</dialog>>
<<dialog "parkRunnerLily">>Shh. Don’t ruin my reputation.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
"paths_and_pacing": [
{
text: `<<narrative "Sunset Park">>Lily points along the winding path with her water bottle, tracing an invisible route in the air.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_afternoon_paths_and_pacing_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>If you’re new here, start with the inner loop. It’s shorter and your calves won’t hate you tomorrow morning.<</dialog>>
<<dialog "player">>Sounds like experience talking.<</dialog>>
<<dialog "parkRunnerLily">>Experience, yes. Dignity… that’s still debatable.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<narrative "Sunset Park">>Kids are running around on the grass nearby. Lily watches them with a soft smile before turning back to you.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_afternoon_paths_and_pacing_2.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Afternoons are when the park turns into a whole little town. Dogs, strollers, chaos… I secretly love it.<</dialog>>
<<dialog "player">>Doesn’t it mess with your pace?<</dialog>>
<<dialog "parkRunnerLily">>That’s what headphones and good dodging skills are for.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"gear_chat": [
{
text: `<<narrative "Sunset Park">>Lily tugs at the bottom of her running shirt, checking the fit.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_afternoon_gear_chat_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Real talk if you go shopping for gear, actually move around in it at the store. Mirrors lie. Your body doesn’t.<</dialog>>
<<dialog "player">>Squats in the fitting room?<</dialog>>
<<dialog "parkRunnerLily">>High knees if you’re brave. Lunges if you want the employees to remember you forever.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
},
"sunsetPark_common_evening": {
"golden_hour": [
{
text: `<<narrative "Sunset Park">>The light over the pond has turned soft and golden. Lily’s pace is relaxed now, like she’s unwinding with the day.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_evening_golden_hour_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Evening runs are my favorite reward. The whole world just… softens.<</dialog>>
<<dialog "player">>That sounds really nice.<</dialog>>
<<dialog "parkRunnerLily">>It is. Bring a jacket though Maplewood evenings love to trick you.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<narrative "Sunset Park">>Lily stretches her arms overhead, her silhouette sharp against the glowing sky.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_evening_golden_hour_2.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>If you time it right you get the sunset and the duck drama for free. Best show in town.<</dialog>>
<<dialog "player">>Duck drama?<</dialog>>
<<dialog "parkRunnerLily">>Oh yeah. Bread politics, territorial quacks… it gets intense.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"wind_down": [
{
text: `<<narrative "Sunset Park">>The air is starting to cool. Fireflies haven’t appeared yet, but the park already feels like it’s heading toward night.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_evening_wind_down_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>After a long day this is my reset button. Sweat out the nonsense, walk home feeling human again.<</dialog>>
<<dialog "player">>Does it actually work?<</dialog>>
<<dialog "parkRunnerLily">>Most days. The nonsense always tries to sneak back in, but I win the first round.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<narrative "Sunset Park">>Lily sits on a bench and starts untying her shoes with obvious relief.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_evening_wind_down_2.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>If you ever see me out here looking half-dead, don’t worry. It’s the good kind of tired.<</dialog>>
<<dialog "player">>Noted.<</dialog>>
<<dialog "parkRunnerLily">>The bad kind is when you skip stretching. Learn from my many mistakes.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"invite_back": [
{
text: `<<narrative "Sunset Park">>The path lamps start flickering on one by one, casting a gentle glow through the trees.<</narrative>>
<<set _img = "assets/content/scenes/interactions/lily/park/level1/sunsetPark_common_evening_invite_back_1.webp">>
<<image _img "100%">>
<<dialog "parkRunnerLily">>Same offer as always if you want company, I’m usually out here around dusk. No pressure, no pace shaming.<</dialog>>
<<dialog "player">>I’ll keep that in mind.<</dialog>>
<<dialog "parkRunnerLily">>Good. This park’s way better when you share it with someone.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.marcusTopics = setup.marcusTopics || {}>>
<<set setup.marcusTopics.common = setup.marcusTopics.common || {}>>
<<set setup.marcusTopics.common.level1 = {
"storeCorner_common_morning": {
"open_shift": [
{
text: `<<narrative "Quick Mart - Counter">>Marcus closes the drink cooler, humming quietly to himself. When the door chimes, he straightens up with a bright, energetic smile, as if he's been awake for hours.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_morning_open_shift_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Morning! You're out early. Coffee's still brewing, but the energy drinks are ice cold if you need a quick boost.<</dialog>>
<<dialog "player">>I'm mostly awake already. How are you this energetic so early?<</dialog>>
<<dialog "shopClerkMarcus">>Opening shift is my favorite. The store's quiet, everything's in its place, and nobody's spilled a Slushie yet. I'll take this peace while it lasts.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<narrative "Quick Mart - Counter">>Marcus wipes the counter with quick, practiced strokes while soft pop music plays low on the radio. He looks up as you enter.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_morning_open_shift_2.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Hey, early bird strikes again. I just finished the opening checklisttwenty minutes of beeps and no customers.<</dialog>>
<<dialog "player">>Sounds like a morning ritual.<</dialog>>
<<dialog "shopClerkMarcus">>It really is. Wakes my brain up. After that, I'm ready for whatever Maplewood throws at me.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
"neighborhood_energy": [
{
text: `<<narrative "Quick Mart - Counter">>Marcus neatly stocks bags of chips, then taps the display proudly when he notices you.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_morning_neighborhood_energy_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Mornings in retail are kinda nice. You get to see the same joggers, dog walkers, and regulars. Maplewood runs like a little clock.<</dialog>>
<<dialog "player">>You really notice all that?<</dialog>>
<<dialog "shopClerkMarcus">>Hard not to. Recognizing faces makes the shift feel a lot less endless.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<narrative "Quick Mart - Counter">>Marcus counts bills behind the register, then looks up with a warm smile.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_morning_neighborhood_energy_2.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Good morning! The pastries just came inthey're still warm if you're grabbing breakfast.<</dialog>>
<<dialog "player">>That actually sounds tempting.<</dialog>>
<<dialog "shopClerkMarcus">>That's the spirit. Fuel up before the day starts throwing errands at you.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"map_followup": [
{
text: `<<narrative "Quick Mart - Counter">>Marcus caps a marker after finishing a sale sign and grins when you walk in.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_morning_map_followup_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Hey! Did that map actually help, or did it become a crumpled ball in your pocket?<</dialog>>
<<dialog "player">>It helped. I'm not getting lost on my own block anymore.<</dialog>>
<<dialog "shopClerkMarcus">>Nice! If you discover any weird shortcuts, you owe me the gossip.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
},
"storeCorner_common_afternoon": {
"mid_shift": [
{
text: `<<narrative "Quick Mart - Counter">>The counter is still cluttered with receipts from the lunch rush. Marcus sorts them into a neat pile, sleeves rolled up.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_afternoon_mid_shift_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Finally, the afternoon lull. For a solid hour it was just beeps and people asking for the bathroom.<</dialog>>
<<dialog "player">>Sounds rough.<</dialog>>
<<dialog "shopClerkMarcus">>Busy is okay. It's the noise that wears you down. Now I can hear myself think again.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<narrative "Quick Mart - Counter">>Marcus leans on the counter, slowly rolling his shoulders like they're stiff.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_afternoon_mid_shift_2.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Official mid-shift stretch break. If I don't move soon, I'm gonna fuse with the register.<</dialog>>
<<dialog "player">>Retail yoga?<</dialog>>
<<dialog "shopClerkMarcus">>Exactly. Very spiritual. Very creaky.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"heat_snacks": [
{
text: `<<narrative "Quick Mart - Counter">>The AC hums steadily as Marcus rearranges bottles in the cold case, fog spilling out.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_afternoon_heat_snacks_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>This afternoon heat hits different through the glass doors. People come in for ice and walk out with ice plus three unplanned items.<</dialog>>
<<dialog "player">>Classic convenience store math.<</dialog>>
<<dialog "shopClerkMarcus">>I'm not judging. I've done the exact same thing on my breaks.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<narrative "Quick Mart - Counter">>A half-emptied delivery box sits behind the counter. Marcus cuts it open and checks items off his list.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_afternoon_heat_snacks_2.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Delivery day crashing into lunch rushalways fun. At least the stockroom smells like soap and chips instead of the dumpster.<</dialog>>
<<dialog "player">>The glamorous side of retail.<</dialog>>
<<dialog "shopClerkMarcus">>Hey, I signed up for fluorescent lights and mystery beeps. Living the dream.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"regulars": [
{
text: `<<narrative "Quick Mart - Counter">>Marcus refills the coffee urn, the sharp bitter smell filling the air.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_afternoon_regulars_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Afternoon regulars are a whole different breed. Slower steps, bigger sighs, and way more energy drinks.<</dialog>>
<<dialog "player">>Which category do I fall into?<</dialog>>
<<dialog "shopClerkMarcus">>You're in the "actually says hi" category. That automatically puts you in the top tier.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
},
"storeCorner_common_evening": {
"closing_energy": [
{
text: `<<narrative "Quick Mart - Counter">>The light coming through the windows has turned amber. Marcus sweeps the floor with slow but steady strokes.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_evening_closing_energy_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>The evening crew is already counting down in my head. Not grumpy, just... volume turned down a bit.<</dialog>>
<<dialog "player">>Long day?<</dialog>>
<<dialog "shopClerkMarcus">>Full day. Still nice that you stopped by. Friendly faces make the last hour easier.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<narrative "Quick Mart - Counter">>Marcus dims some of the overhead lights and straightens the impulse-buy rack again.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_evening_closing_energy_2.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Evening shoppers either forgot something for dinner or they're avoiding cooking. I get both.<</dialog>>
<<dialog "player">>Sometimes it's both for me.<</dialog>>
<<dialog "shopClerkMarcus">>Then you're my kind of people. Grab something easy and call it a win.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"winding_down": [
{
text: `<<narrative "Quick Mart - Counter">>Marcus leans on the counter with his forearms, phone face-down beside the register. He gives you a tired but genuine smile.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_evening_winding_down_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Hey. If you need something from the top shelf, give me a warningmy vertical jump isn't what it was at 9 a.m.<</dialog>>
<<dialog "player">>Just came by to say hi.<</dialog>>
<<dialog "shopClerkMarcus">>I'll take it. Way better than arguing with the card reader.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<narrative "Quick Mart - Counter">>Headlights sweep across the windows outside. Inside, the store feels smaller and cozier. Marcus counts cigarettes, lips moving slightly.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_evening_winding_down_2.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Evening math: cigarettes, lottery tickets, and the guy who always buys the same chocolate bar. Brains love patterns.<</dialog>>
<<dialog "player">>Does yours work like that too?<</dialog>>
<<dialog "shopClerkMarcus">>Especially mine. I could probably run this place blindfolded. Don't tell my manager.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
"night_coming": [
{
text: `<<narrative "Quick Mart - Counter">>The door hisses as a customer leaves. Marcus rolls his neck, then spots you.<</narrative>>
<<set _img = "assets/content/scenes/interactions/marcus/level1/storeCorner_common_evening_night_coming_1.webp">>
<<image _img "100%">>
<<dialog "shopClerkMarcus">>Sun's basically gone. Time for the "do I really need this snack?" crowd. Spoiler: they always do.<</dialog>>
<<dialog "player">>Honest marketing.<</dialog>>
<<dialog "shopClerkMarcus">>I call it gentle enabling. We're all just trying to survive out here.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set _charId = $interactingChar>>
<<set _placeId = $meetupReturnLocation || $meetupLocation || $location>>
<<set _returnLoc = _placeId>>
<<set _returnLocName = _returnLoc ? (setup.navCards[_returnLoc]?.name || _returnLoc) : "location">>
<<set _char = setup.getCharacter(_charId)>>
<<set _charName = _char ? ((_char.firstName || _char.name || "") + (_char.lastName ? " " + _char.lastName : "")) : _charId>>
<<if def "touchCharacterRelation">>
<<touchCharacterRelation _charId>>
<</if>>
<<narrative "Meetup">>
You met <<print _charName>> at <<print _returnLocName>>. What would you like to do?
<</narrative>>
<div class="location-actions meetup-actions">
<<if _placeId === "sunsetPark">>
<<link "Sit on bench">>
<<set $meetupAction = "bench">>
<<goto "Meetup_Park_Bench">>
<</link>>
<<link "Take a walk">>
<<set $meetupAction = "walk">>
<<goto "Meetup_Park_Walk">>
<</link>>
<<elseif _placeId === "dinerRubys">>
<<link "Eat food">>
<<set $meetupAction = "eat">>
<<goto "Meetup_Diner_Eat">>
<</link>>
<<else>>
<<link "Chat">>
<<set $meetupAction = "chat">>
<<goto "Meetup_Generic_Chat">>
<</link>>
<</if>>
<<link "Back to " + _returnLocName _returnLoc "secondary">><</link>>
</div>
<</nobr>><<nobr>>
<<set _charId = $interactingChar>>
<<set _returnLoc = $meetupReturnLocation || "dinerRubys">>
<<set _char = setup.getCharacter(_charId)>>
<<set _charName = _char ? ((_char.firstName || _char.name || "") + (_char.lastName ? " " + _char.lastName : "")) : _charId>>
<<silently>>
<<completeMeetupAppointment>>
<<advanceTime 45 "relax">>
<<loseStat "hunger" 40>>
<<gainStat "energy" 10>>
<<gainStat "mood" 5>>
<<set _curFriend = $characters[_charId].stats.friendship || 0>>
<<set $characters[_charId].stats.friendship = Math.min(100, _curFriend + 3)>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative "Meetup - Diner">>
You and <<print _charName>> share a meal at the diner. Good food and good company – you chat over burgers and drinks. A nice way to catch up.
<</narrative>>
<div class="location-actions">
<<btn "Continue" _returnLoc>><</btn>>
</div>
<</nobr>><<nobr>>
<<set _charId = $interactingChar>>
<<set _returnLoc = $meetupReturnLocation || $location>>
<<set _char = setup.getCharacter(_charId)>>
<<set _charName = _char ? ((_char.firstName || _char.name || "") + (_char.lastName ? " " + _char.lastName : "")) : _charId>>
<<silently>>
<<completeMeetupAppointment>>
<<advanceTime 20>>
<<gainStat "mood" 3>>
<<set _curFriend = $characters[_charId].stats.friendship || 0>>
<<set $characters[_charId].stats.friendship = Math.min(100, _curFriend + 2)>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative "Meetup">>
You spend some time chatting with <<print _charName>>. The conversation is relaxed and enjoyable.
<</narrative>>
<div class="location-actions">
<<btn "Continue" _returnLoc>><</btn>>
</div>
<</nobr>><<nobr>>
<<set _charId = $interactingChar>>
<<set _returnLoc = $meetupReturnLocation || "sunsetPark">>
<<set _char = setup.getCharacter(_charId)>>
<<set _charName = _char ? ((_char.firstName || _char.name || "") + (_char.lastName ? " " + _char.lastName : "")) : _charId>>
<<silently>>
<<completeMeetupAppointment>>
<<advanceTime 15>>
<<gainStat "mood" 3>>
<<loseStat "stress" 5>>
<<set _curFriend = $characters[_charId].stats.friendship || 0>>
<<set $characters[_charId].stats.friendship = Math.min(100, _curFriend + 2)>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative "Meetup - Park Bench">>
You and <<print _charName>> sit down on a bench overlooking the pond. You chat about nothing in particular – the weather, the people passing by. It's a nice, relaxed moment together.
<</narrative>>
<div class="location-actions">
<<btn "Continue" _returnLoc>><</btn>>
</div>
<</nobr>><<nobr>>
<<set _charId = $interactingChar>>
<<set _returnLoc = $meetupReturnLocation || "sunsetPark">>
<<set _char = setup.getCharacter(_charId)>>
<<set _charName = _char ? ((_char.firstName || _char.name || "") + (_char.lastName ? " " + _char.lastName : "")) : _charId>>
<<silently>>
<<completeMeetupAppointment>>
<<advanceTime 30>>
<<loseStat "energy" 5>>
<<gainStat "mood" 5>>
<<set _curFriend = $characters[_charId].stats.friendship || 0>>
<<set $characters[_charId].stats.friendship = Math.min(100, _curFriend + 3)>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative "Meetup - Walk">>
You and <<print _charName>> take a stroll along the park paths. The conversation flows easily as you walk past trees and the pond. A pleasant way to spend some time together.
<</narrative>>
<div class="location-actions">
<<btn "Continue" _returnLoc>><</btn>>
</div>
<</nobr>>/* ==========================================
MIKE TALK TOPICS COMMON PHASE, LEVEL 1
phase === "common" && friendshipLevel === 1
CHARACTER: Mike, Dishwasher (dinerDishwasher)
→ Laid-back, young, easygoing, lazy-but-friendly.
→ Rare at front (Front access is only during Common).
========================================== */
<<nobr>>
<<set setup.mikeTopics = setup.mikeTopics || {}>>
<<set setup.mikeTopics.common = setup.mikeTopics.common || {}>>
<<set setup.mikeTopics.common.level1 = {
dinerRubys_common_morning: {
out_of_place_morning: [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontMorning-1.webp">>
<<image _img "100%">>
<<narrative>>Mike is blinking at the morning sun, holding a mop bucket like he's not quite sure what to do with it.<</narrative>>
<<dialog "dinerDishwasher">>Is it morning already? Felt like I just closed my eyes.<</dialog>>
<<dialog "player">>Usually happens when you stay up too late, Mike.<</dialog>>
<<dialog "dinerDishwasher">>Yeah. Found a documentary on deep-sea squids. Couldn't stop.<</dialog>>
<<dialog "player">>Deep-sea squids. Riveting stuff.<</dialog>>
<<dialog "dinerDishwasher">>You joke, but they're basically aliens, $player.firstName. Real aliens.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontMorning-2.webp">>
<<image _img "100%">>
<<narrative>>Mike is leaning against the counter, staring at the fresh donuts in the case with a glazed expression.<</narrative>>
<<dialog "dinerDishwasher">>One of these days, I'm gonna eat the whole case.<</dialog>>
<<dialog "player">>Vince would probably charge you triple.<</dialog>>
<<dialog "dinerDishwasher">>He'd have to catch me first. I'm faster than I look.<</dialog>>
<<dialog "player">>I've seen you walk to the kitchen, Mike. You're not fast.<</dialog>>
<<dialog "dinerDishwasher">>Low-power mode. Saves energy for the donuts.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontMorning-3.webp">>
<<image _img "100%">>
<<narrative>>Mike has one earbud in, swaying slightly as he slowly refills the sugar packets at a booth.<</narrative>>
<<dialog "dinerDishwasher">>You catch the sunrise? Orange and purple. Pretty sick.<</dialog>>
<<dialog "player">>I was asleep for that part of the day.<</dialog>>
<<dialog "dinerDishwasher">>Missing out, man. Best part of the day before the noise starts.<</dialog>>
<<dialog "player">>You're surprisingly poetic for a guy who sleeps through his shift.<</dialog>>
<<dialog "dinerDishwasher">>I don't sleep. I meditate on the bubbles in the sink.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
],
morning_daydream: [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontMorning-4.webp">>
<<image _img "100%">>
<<narrative>>Mike stands near the register with a distant look, holding a half-full coffee cup he never seems to drink.<</narrative>>
<<dialog "dinerDishwasher">>You ever wonder what fish think about?<</dialog>>
<<dialog "player">>Can't say I have.<</dialog>>
<<dialog "dinerDishwasher">>Like, they're just in there. Swimming. No bills.<</dialog>>
<<dialog "player">>That's the dream, apparently.<</dialog>>
<<dialog "dinerDishwasher">>Exactly. I get it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontMorning-5.webp">>
<<image _img "100%">>
<<narrative>>Mike is staring at a coffee stain on the ceiling. He doesn't acknowledge you right away.<</narrative>>
<<dialog "dinerDishwasher">>That stain has been there since I started.<</dialog>>
<<dialog "player">>Nobody cleaned it?<</dialog>>
<<dialog "dinerDishwasher">>Nah. It's kind of like a landmark now.<</dialog>>
<<dialog "player">>A ceiling landmark.<</dialog>>
<<dialog "dinerDishwasher">>My ceiling landmark. I feel responsible for it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontMorning-6.webp">>
<<image _img "100%">>
<<narrative>>Mike slouches against the counter, watching a fly circle a table. He tracks it slowly with his eyes.<</narrative>>
<<dialog "dinerDishwasher">>That fly's been in here all week.<</dialog>>
<<dialog "player">>Seriously? Same fly?<</dialog>>
<<dialog "dinerDishwasher">>I think so. He's got a pattern.<</dialog>>
<<dialog "player">>You've been watching a fly's patterns.<</dialog>>
<<dialog "dinerDishwasher">>Slow morning. He's more interesting than the menu.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubys_common_afternoon: {
out_of_place_afternoon: [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Mike stands near the counter, blinking at the sunlight coming through the front window.<</narrative>>
<<dialog "dinerDishwasher">>Is it always this bright out here?<</dialog>>
<<dialog "player">>It's just the afternoon sun, Mike.<</dialog>>
<<dialog "dinerDishwasher">>Yeah. Makes the kitchen look like a cave.<</dialog>>
<<dialog "player">>You should come out more often.<</dialog>>
<<dialog "dinerDishwasher">>Maybe. But caves have better acoustics.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Mike is leaning against the pastry case, staring at a pie.<</narrative>>
<<dialog "dinerDishwasher">>You ever notice how symmetrical these pies are?<</dialog>>
<<dialog "player">>They're just pies, Mike. James makes them.<</dialog>>
<<dialog "dinerDishwasher">>James is an artist, man. A stressed-out artist.<</dialog>>
<<dialog "player">>Don't let him hear you say that.<</dialog>>
<<dialog "dinerDishwasher">>I don't. I just clean his canvases.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Mike is peeking through the order window from the front side.<</narrative>>
<<dialog "dinerDishwasher">>They look so busy in there. Glad I'm on this side.<</dialog>>
<<dialog "player">>You're supposed to be in there helping them, aren't you?<</dialog>>
<<dialog "dinerDishwasher">>Spiritually, I am. Physically, I need a change of scenery.<</dialog>>
<<dialog "player">>Vince won't like your change of scenery.<</dialog>>
<<dialog "dinerDishwasher">>Vince is in his office. My scenery is safe for now.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
music_talk: [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Mike has one earbud in, tapping his fingers against his thigh.<</narrative>>
<<dialog "dinerDishwasher">>The music out here is too... diner-ish.<</dialog>>
<<dialog "player">>Well, it is a diner. What do you expect?<</dialog>>
<<dialog "dinerDishwasher">>Needs more bass. Something to vibe with.<</dialog>>
<<dialog "player">>Vince probably likes the classics.<</dialog>>
<<dialog "dinerDishwasher">>Vince likes whatever keeps people eating. I like whatever keeps me awake.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Mike is humming a tune under his breath, eyes closed for a moment.<</narrative>>
<<dialog "dinerDishwasher">>Sorry. Song got stuck in my head.<</dialog>>
<<dialog "player">>Sounded like a lot of drums.<</dialog>>
<<dialog "dinerDishwasher">>It's all drums. Drums are the heartbeat of the kitchen, you know?<</dialog>>
<<dialog "player">>I thought the sizzling grill was the heartbeat.<</dialog>>
<<dialog "dinerDishwasher">>That's just the noise. Drums are the soul.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-7.webp">>
<<image _img "100%">>
<<narrative>>Mike is staring at the old jukebox in the corner.<</narrative>>
<<dialog "dinerDishwasher">>Think this thing still works? Record-style?<</dialog>>
<<dialog "player">>Probably. It's mostly for show though.<</dialog>>
<<dialog "dinerDishwasher">>What a waste. I'd put some real heavy stuff in there.<</dialog>>
<<dialog "player">>You'd clear the room in under a minute.<</dialog>>
<<dialog "dinerDishwasher">>Yeah, but what a minute it would be.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
food_observations: [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-8.webp">>
<<image _img "100%">>
<<narrative>>Mike squints at the daily special board, tilting his head slightly.<</narrative>>
<<dialog "dinerDishwasher">>The meatloaf special looks different today.<</dialog>>
<<dialog "player">>Different how?<</dialog>>
<<dialog "dinerDishwasher">>Smaller. James is having a mood.<</dialog>>
<<dialog "player">>Does meatloaf size reflect his moods?<</dialog>>
<<dialog "dinerDishwasher">>Every time. It's a system.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-9.webp">>
<<image _img "100%">>
<<narrative>>Mike stares at a slice of pie in the display case, arms folded.<</narrative>>
<<dialog "dinerDishwasher">>That pie has been there all day. Nobody's ordered it.<</dialog>>
<<dialog "player">>Maybe people prefer the burgers today.<</dialog>>
<<dialog "dinerDishwasher">>It's starting to feel personal.<</dialog>>
<<dialog "player">>The pie isn't taking it personally.<</dialog>>
<<dialog "dinerDishwasher">>I'm taking it personally. I wash those pie plates.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeFrontAfternoon-10.webp">>
<<image _img "100%">>
<<narrative>>Mike carries a tray of clean mugs and sets them down with zero urgency.<</narrative>>
<<dialog "dinerDishwasher">>Coffee mug count today: forty-three.<</dialog>>
<<dialog "player">>You counted the mugs?<</dialog>>
<<dialog "dinerDishwasher">>Lost count twice. Third time I committed.<</dialog>>
<<dialog "player">>Why?<</dialog>>
<<dialog "dinerDishwasher">>Needed a goal for the afternoon.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.mikeTopics = setup.mikeTopics || {}>>
<<set setup.mikeTopics.dishwasherDone = setup.mikeTopics.dishwasherDone || {}>>
<<set setup.mikeTopics.dishwasherDone.level1 = {
"dinerRubysKitchen_dishwasherDone_morning": {
"shift_envy": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-7.webp">>
<<image _img "100%">>
<<narrative>>Mike glances over from the sink as you pass through in street clothes. He shakes his head slowly.<</narrative>>
<<dialog "dinerDishwasher">>Done already? That's not even fair.<</dialog>>
<<dialog "player">>Short shift today. Got lucky.<</dialog>>
<<dialog "dinerDishwasher">>I don't get lucky. I get pots.<</dialog>>
<<dialog "player">>Hang in there, Mike.<</dialog>>
<<dialog "dinerDishwasher">>Yeah. I'll try. Go be free.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-8.webp">>
<<image _img "100%">>
<<narrative>>Mike leans on the edge of the sink, watching you grab your stuff. Suds drip from his rubber gloves.<</narrative>>
<<dialog "dinerDishwasher">>Must be nice. Walking out while the sun's still up.<</dialog>>
<<dialog "player">>You'll be done eventually too.<</dialog>>
<<dialog "dinerDishwasher">>Eventually is doing a lot of heavy lifting there.<</dialog>>
<<dialog "player">>Want me to bring you back a coffee or something?<</dialog>>
<<dialog "dinerDishwasher">>Nah. I'll survive. Probably. See you.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-9.webp">>
<<image _img "100%">>
<<narrative>>Mike rinses a tray without looking at it, eyes on you heading for the door.<</narrative>>
<<dialog "dinerDishwasher">>Leaving me alone with the dishes. Cold.<</dialog>>
<<dialog "player">>They were your dishes before I got here.<</dialog>>
<<dialog "dinerDishwasher">>True. But misery loves company.<</dialog>>
<<dialog "player">>You'll be fine. See you tomorrow.<</dialog>>
<<dialog "dinerDishwasher">>Yeah yeah. Later.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"winding_down": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-10.webp">>
<<image _img "100%">>
<<narrative>>Mike slowly wipes down the counter next to the sink. He's moving even slower than usual.<</narrative>>
<<dialog "dinerDishwasher">>Morning's almost over. Thank god.<</dialog>>
<<dialog "player">>You heading out soon too?<</dialog>>
<<dialog "dinerDishwasher">>Couple more hours. Lunch rush dishes.<</dialog>>
<<dialog "player">>That sounds rough after a full morning.<</dialog>>
<<dialog "dinerDishwasher">>I just stop thinking about it. Works great.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-11.webp">>
<<image _img "100%">>
<<narrative>>Mike stretches his arms and shakes water off his hands. He yawns wide enough to hear it.<</narrative>>
<<dialog "dinerDishwasher">>I've washed like two hundred plates today.<</dialog>>
<<dialog "player">>You actually counted?<</dialog>>
<<dialog "dinerDishwasher">>Nah. But it feels like two hundred.<</dialog>>
<<dialog "player">>Impressive either way.<</dialog>>
<<dialog "dinerDishwasher">>Put it on my resume. Plate guy.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-12.webp">>
<<image _img "100%">>
<<narrative>>Mike peels off one rubber glove and flexes his pruney fingers with a grimace.<</narrative>>
<<dialog "dinerDishwasher">>Look at my hands. I'm aging in fast forward.<</dialog>>
<<dialog "player">>Dishpan hands. Classic.<</dialog>>
<<dialog "dinerDishwasher">>My grandma used to say that. Thanks.<</dialog>>
<<dialog "player">>At least you're done for the morning, right?<</dialog>>
<<dialog "dinerDishwasher">>Close enough. I'm mentally done. That counts.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysKitchen_dishwasherDone_afternoon": {
"almost_free": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Mike stacks the last few clean pans on the shelf, moving like he's already checked out mentally.<</narrative>>
<<dialog "dinerDishwasher">>Afternoon shift's the worst. Drags forever.<</dialog>>
<<dialog "player">>At least it's almost over now.<</dialog>>
<<dialog "dinerDishwasher">>Almost is the most dangerous word. Jinx it and more plates show up.<</dialog>>
<<dialog "player">>I'll keep quiet then.<</dialog>>
<<dialog "dinerDishwasher">>Smart. Very smart.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Mike drains the sink and watches the water spiral down. He stares at it like it's a meditation exercise.<</narrative>>
<<dialog "dinerDishwasher">>Last drain of the day. Beautiful sight.<</dialog>>
<<dialog "player">>You're weirdly poetic about dishwater.<</dialog>>
<<dialog "dinerDishwasher">>When it's going down, it means I'm going home.<</dialog>>
<<dialog "player">>Got any plans for tonight?<</dialog>>
<<dialog "dinerDishwasher">>Couch. TV. Maybe food. Big night.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Mike pulls his apron over his head and folds it loosely. He tosses it toward the hook. Misses.<</narrative>>
<<dialog "dinerDishwasher">>Close enough. I'm off the clock mentally.<</dialog>>
<<dialog "player">>Your aim says the same thing.<</dialog>>
<<dialog "dinerDishwasher">>Hey, I hit it like forty percent of the time.<</dialog>>
<<dialog "player">>Those are not great odds.<</dialog>>
<<dialog "dinerDishwasher">>Good enough for dishwasher work.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"after_work_plans": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Mike checks his phone quickly while drying his hands on a rag. He smirks at something on the screen.<</narrative>>
<<dialog "dinerDishwasher">>Buddy wants to play games tonight. I'm in.<</dialog>>
<<dialog "player">>What do you guys play?<</dialog>>
<<dialog "dinerDishwasher">>Whatever's free. We're not fancy.<</dialog>>
<<dialog "player">>Sounds like a solid night honestly.<</dialog>>
<<dialog "dinerDishwasher">>Better than scrubbing pans. Low bar though.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Mike leans against the counter, arms crossed, watching the clock on the wall. His shift's winding down.<</narrative>>
<<dialog "dinerDishwasher">>Gonna go home and do absolutely nothing.<</dialog>>
<<dialog "player">>No plans at all?<</dialog>>
<<dialog "dinerDishwasher">>That IS the plan. Nothing. On purpose.<</dialog>>
<<dialog "player">>Intentional nothing. I respect that.<</dialog>>
<<dialog "dinerDishwasher">>It's an art form. I've been practicing.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Mike hangs up his rubber gloves on the faucet and shakes out his hands one last time.<</narrative>>
<<dialog "dinerDishwasher">>Freedom in like twenty minutes. I can smell it.<</dialog>>
<<dialog "player">>That might just be the bleach.<</dialog>>
<<dialog "dinerDishwasher">>Nah, freedom smells different. More like pizza.<</dialog>>
<<dialog "player">>Stopping somewhere on the way home?<</dialog>>
<<dialog "dinerDishwasher">>There's a place on the way. Two slices and I'm good.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherDone_morning": {
"done_for_now": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-7.webp">>
<<image _img "100%">>
<<narrative>>Mike sits on an upturned bucket in the storage room, earbuds around his neck. He looks completely decompressed.<</narrative>>
<<dialog "dinerDishwasher">>Made it. Morning's done.<</dialog>>
<<dialog "player">>You look like you just survived something.<</dialog>>
<<dialog "dinerDishwasher">>Breakfast rush dishes. Basically a war zone.<</dialog>>
<<dialog "player">>That dramatic, huh?<</dialog>>
<<dialog "dinerDishwasher">>You weren't back there. You don't know.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-8.webp">>
<<image _img "100%">>
<<narrative>>Mike leans against the shelves, sipping something from a paper cup. He's in no rush to move.<</narrative>>
<<dialog "dinerDishwasher">>Hiding back here till Vince forgets I exist.<</dialog>>
<<dialog "player">>Think that'll work?<</dialog>>
<<dialog "dinerDishwasher">>Worked last Tuesday. I'm optimistic.<</dialog>>
<<dialog "player">>Bold strategy. Good luck with that.<</dialog>>
<<dialog "dinerDishwasher">>Thanks. I'll need it.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-9.webp">>
<<image _img "100%">>
<<narrative>>Mike has his jacket on already, sitting on a stack of flour bags. He's ready to leave ten minutes early.<</narrative>>
<<dialog "dinerDishwasher">>I'm basically already gone. Spiritually.<</dialog>>
<<dialog "player">>Your body's still here though.<</dialog>>
<<dialog "dinerDishwasher">>Minor detail. My soul left at like nine.<</dialog>>
<<dialog "player">>That early?<</dialog>>
<<dialog "dinerDishwasher">>Didn't even clock in all the way mentally. Happens.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"chill_goodbye": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-10.webp">>
<<image _img "100%">>
<<narrative>>Mike scrolls his phone with one hand, the other resting on his knee. He barely looks up.<</narrative>>
<<dialog "dinerDishwasher">>You leaving too? Nice.<</dialog>>
<<dialog "player">>Yeah, shift's over. Heading out.<</dialog>>
<<dialog "dinerDishwasher">>Walk slow. Savor it.<</dialog>>
<<dialog "player">>You say that like it's a big deal.<</dialog>>
<<dialog "dinerDishwasher">>Leaving work is always a big deal.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-11.webp">>
<<image _img "100%">>
<<narrative>>Mike gives a lazy two-finger wave from his spot on the crate. He doesn't stand up.<</narrative>>
<<dialog "dinerDishwasher">>Later. Good shift. Didn't break anything.<</dialog>>
<<dialog "player">>Is that the bar? Not breaking things?<</dialog>>
<<dialog "dinerDishwasher">>Around here? Yeah. That's a win.<</dialog>>
<<dialog "player">>Alright then. See you next time, Mike.<</dialog>>
<<dialog "dinerDishwasher">>See you. Don't come back too early.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-12.webp">>
<<image _img "100%">>
<<narrative>>Mike stretches long and slow, then settles back into the same position. Like a cat that moved just to get comfortable again.<</narrative>>
<<dialog "dinerDishwasher">>Whole day ahead of you. What's the move?<</dialog>>
<<dialog "player">>Not sure yet. Maybe just relax.<</dialog>>
<<dialog "dinerDishwasher">>Good answer. Don't overthink it.<</dialog>>
<<dialog "player">>Spoken like a true expert.<</dialog>>
<<dialog "dinerDishwasher">>Relaxing is the one thing I'm actually good at.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.mikeTopics = setup.mikeTopics || {}>>
<<set setup.mikeTopics.dishwasherOnBreak = setup.mikeTopics.dishwasherOnBreak || {}>>
<<set setup.mikeTopics.dishwasherOnBreak.level1 = {
"dinerRubysKitchen_dishwasherOnBreak_morning": {
"headphones_music": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>Mike stands at the sink, one earbud in, nodding along to something. He mouths a few words before noticing you.<</narrative>>
<<dialog "dinerDishwasher">>Oh. Hey. Didn't hear you.<</dialog>>
<<dialog "player">>What are you listening to?<</dialog>>
<<dialog "dinerDishwasher">>Some playlist. All vibes, no lyrics.<</dialog>>
<<dialog "player">>Sounds like a good way to zone out.<</dialog>>
<<dialog "dinerDishwasher">>That's the whole point.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>Mike bobs his head gently while loading plates into the rack. The earbud cord swings with each movement.<</narrative>>
<<dialog "dinerDishwasher">>This song's been stuck in my head all week.<</dialog>>
<<dialog "player">>What song is it?<</dialog>>
<<dialog "dinerDishwasher">>No idea. Found it on autoplay.<</dialog>>
<<dialog "player">>You don't even know the name?<</dialog>>
<<dialog "dinerDishwasher">>Nah. That's the best kind of music though.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>Mike pulls an earbud out and drapes it over his shoulder. The sink is half full of mugs.<</narrative>>
<<dialog "dinerDishwasher">>Podcast ended. Now it's just me and these mugs.<</dialog>>
<<dialog "player">>What podcast were you listening to?<</dialog>>
<<dialog "dinerDishwasher">>True crime thing. Guy who robbed a pizza place.<</dialog>>
<<dialog "player">>That sounds kind of amazing actually.<</dialog>>
<<dialog "dinerDishwasher">>It was. He got caught by the delivery driver.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"morning_dishes": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-4.webp">>
<<image _img "100%">>
<<narrative>>Mike scrubs a pan with zero urgency, steam rising from the hot water. He glances over his shoulder at you.<</narrative>>
<<dialog "dinerDishwasher">>Egg pans. Worst part of mornings.<</dialog>>
<<dialog "player">>They really stick that bad?<</dialog>>
<<dialog "dinerDishwasher">>Like cement if you wait too long.<</dialog>>
<<dialog "player">>Sounds like nobody brings them fast enough.<</dialog>>
<<dialog "dinerDishwasher">>Nobody ever does. Story of my life.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-5.webp">>
<<image _img "100%">>
<<narrative>>Mike stacks clean plates onto a shelf, moving slowly. A tower of dirty ones waits behind him.<</narrative>>
<<dialog "dinerDishwasher">>One stack done. Twelve to go.<</dialog>>
<<dialog "player">>You keeping count?<</dialog>>
<<dialog "dinerDishwasher">>Nah. Makes it worse if I count.<</dialog>>
<<dialog "player">>Fair. Ignorance is bliss, right?<</dialog>>
<<dialog "dinerDishwasher">>Exactly. Don't tell me the real number.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-6.webp">>
<<image _img "100%">>
<<narrative>>Mike pushes a rack into the dishwasher and slams the door shut. He leans on it while it runs.<</narrative>>
<<dialog "dinerDishwasher">>Machine does the hard part now.<</dialog>>
<<dialog "player">>You just stand there while it runs?<</dialog>>
<<dialog "dinerDishwasher">>Best two minutes of my shift.<</dialog>>
<<dialog "player">>Living the dream, huh?<</dialog>>
<<dialog "dinerDishwasher">>Closest I'll get today, yeah.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
kitchen_zone_out: [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-13.webp">>
<<image _img "100%">>
<<narrative>>Mike pauses mid-scrub and stares at the wall for a few seconds. He blinks slowly.<<narrative>>
<<dialog "dinerDishwasher">>Ever just stare at something and forget where you are?<<dialog>>
<<dialog "player">>Usually when I'm very tired.<<dialog>>
<<dialog "dinerDishwasher">>I'm at a five out of ten tired. Just coasting.<<dialog>>
<<dialog "player">>That's a very specific rating system.<<dialog>>
<<dialog "dinerDishwasher">>I have a lot of time to develop it.<<dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-14.webp">>
<<image _img "100%">>
<<narrative>>Mike rinses a stack of glasses, watching the bubbles disappear one by one.<<narrative>>
<<dialog "dinerDishwasher">>Watching bubbles pop is weirdly satisfying.<<dialog>>
<<dialog "player">>I see why you like this job.<<dialog>>
<<dialog "dinerDishwasher">>There are worse perks.<<dialog>>
<<dialog "player">>That's the most positive thing you've said all shift.<<dialog>>
<<dialog "dinerDishwasher">>Don't push it.<<dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeKitchenMorning-15.webp">>
<<image _img "100%">>
<<narrative>>Mike leans against the sink between loads, looking comfortable doing nothing.<<narrative>>
<<dialog "dinerDishwasher">>James yelled three times today. Personal record.<<dialog>>
<<dialog "player">>Yours or his?<<dialog>>
<<dialog "dinerDishwasher">>Mine for surviving it.<<dialog>>
<<dialog "player">>You're adapting.<<dialog>>
<<dialog "dinerDishwasher">>Like water, $player.firstName. Like water.<<dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherOnBreak_morning": {
"phone_scrolling": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-1.webp">>
<<image _img "100%">>
<<narrative>>Mike sits on a crate in the storage room, phone in hand, thumb barely moving. He looks up lazily.<</narrative>>
<<dialog "dinerDishwasher">>Yo. Pull up a crate.<</dialog>>
<<dialog "player">>What are you looking at?<</dialog>>
<<dialog "dinerDishwasher">>Nothing. Literally nothing. Just scrolling.<</dialog>>
<<dialog "player">>That's the most honest answer I've ever heard.<</dialog>>
<<dialog "dinerDishwasher">>I'm an honest guy when I'm bored.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-2.webp">>
<<image _img "100%">>
<<narrative>>Mike tilts his phone screen toward you. It's a video of a cat falling off a counter.<</narrative>>
<<dialog "dinerDishwasher">>Look at this. Watch the landing.<</dialog>>
<<dialog "player">>That cat did not land on its feet.<</dialog>>
<<dialog "dinerDishwasher">>Right? Thought they always did.<</dialog>>
<<dialog "player">>The internet lied to us.<</dialog>>
<<dialog "dinerDishwasher">>Wouldn't be the first time.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-3.webp">>
<<image _img "100%">>
<<narrative>>Mike has his phone face-down on his knee, staring at the ceiling. He's doing absolutely nothing.<</narrative>>
<<dialog "dinerDishwasher">>Battery's at four percent. Day's ruined.<</dialog>>
<<dialog "player">>There's an outlet by the mop bucket.<</dialog>>
<<dialog "dinerDishwasher">>Yeah but then I gotta stand up.<</dialog>>
<<dialog "player">>It's like three steps away, Mike.<</dialog>>
<<dialog "dinerDishwasher">>Three steps too many right now.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"break_vibes": [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-4.webp">>
<<image _img "100%">>
<<narrative>>Mike sips from a can of something, feet stretched out in front of him. He looks completely at peace.<</narrative>>
<<dialog "dinerDishwasher">>This is the life right here.<</dialog>>
<<dialog "player">>Sitting in a storage room drinking soda?<</dialog>>
<<dialog "dinerDishwasher">>No dishes. No James yelling. Perfect.<</dialog>>
<<dialog "player">>Your standards are impressively low.<</dialog>>
<<dialog "dinerDishwasher">>Low standards, high satisfaction. That's the secret.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-5.webp">>
<<image _img "100%">>
<<narrative>>Mike leans against a shelf of paper towel rolls, arms behind his head. He looks half asleep.<</narrative>>
<<dialog "dinerDishwasher">>Almost fell asleep back here once.<</dialog>>
<<dialog "player">>How long before someone noticed?<</dialog>>
<<dialog "dinerDishwasher">>Nobody did. That's the scary part.<</dialog>>
<<dialog "player">>Maybe nobody needed dishes for a while.<</dialog>>
<<dialog "dinerDishwasher">>Or maybe nobody noticed I exist. Either way, win.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-6.webp">>
<<image _img "100%">>
<<narrative>>Mike sits cross-legged on the floor, back against a stack of boxes. He's unwrapping a granola bar.<</narrative>>
<<dialog "dinerDishwasher">>Want half? It's peanut butter.<</dialog>>
<<dialog "player">>Sure, thanks. Didn't eat before my shift.<</dialog>>
<<dialog "dinerDishwasher">>Rookie move. Always eat first.<</dialog>>
<<dialog "player">>Noted. I'll remember that next time.<</dialog>>
<<dialog "dinerDishwasher">>I keep extras in my bag. Just ask.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
],
storage_inventory: [
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-13.webp">>
<<image _img "100%">>
<<narrative>>Mike is halfheartedly counting cans on a shelf. He loses track twice.<<narrative>>
<<dialog "dinerDishwasher">>I'm supposed to do inventory. I hate inventory.<<dialog>>
<<dialog "player">>Isn't it just counting?<<dialog>>
<<dialog "dinerDishwasher">>It's counting AND caring. Too much to ask.<<dialog>>
<<dialog "player">>Fair point.<<dialog>>
<<dialog "dinerDishwasher">>I'm gonna say forty-two and call it done.<<dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-14.webp">>
<<image _img "100%">>
<<narrative>>Mike picks up a can and reads the label with extreme focus, like it might be interesting.<<narrative>>
<<dialog "dinerDishwasher">>Beans. We have so many beans.<<dialog>>
<<dialog "player">>It's a diner. Beans make sense.<<dialog>>
<<dialog "dinerDishwasher">>So many beans. Like, more than needed.<<dialog>>
<<dialog "player">>Maybe Vince bulk-buys?<<dialog>>
<<dialog "dinerDishwasher">>I'm going to think about this for the rest of the shift.<<dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/mike/level1/mikeStorageMorning-15.webp">>
<<image _img "100%">>
<<narrative>>Mike balances a can on his head, staying very still. He looks up to check it's still there.<<narrative>>
<<dialog "dinerDishwasher">>Personal best: thirty-two seconds.<<dialog>>
<<dialog "player">>That's not a record anyone cares about.<<dialog>>
<<dialog "dinerDishwasher">>I care. That's enough.<<dialog>>
<<dialog "player">>You need more hobbies.<<dialog>>
<<dialog "dinerDishwasher">>This IS my hobby.<<dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set _backTo = $mirrorReturnPassage || "fhUpperBath">>
<<set $location = $mirrorReturnPassage || _backTo>>
<<if ndef $makeup>><<set $makeup = { state: "off", style: 0, quality: 0, lastDecayHourKey: "", lastAppliedHourKey: "", lastDecayMinuteStamp: 0 }>><</if>>
<<if ndef $mirrorRoutine>><<set $mirrorRoutine = { makeup: 0, combHair: true, hairCream: true, faceCream: true, dentalCare: true }>><</if>>
<<set _hasRoutine = $mirrorRoutineConfirmed && ($mirrorRoutine.combHair || $mirrorRoutine.hairCream || $mirrorRoutine.faceCream || $mirrorRoutine.dentalCare || ($mirrorRoutine.makeup >= 1))>>
<<checkInventoryItem "makeup_kit" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<set _hasKit = true>>
<<else>>
<<checkInventoryItem "portable_makeup" 1>>
<<set _hasKit = State.temporary.inventoryCheckResult.allowed>>
<</if>>
<<set _corr = $corruption || 0>>
<<set _skill = $skills.practical.makeup || 0>>
<<set _makeupOn = $makeup.state !== "off" && $makeup.style >= 1>>
<<if _makeupOn>><<checkInventoryItem "wet_wipes" 1>><<set _hasWipes = State.temporary.inventoryCheckResult.allowed>><<else>><<set _hasWipes = false>><</if>>
<<narrative "Mirror">>
You stand in front of the mirror. Here you can take care of your appearance.
<</narrative>>
<<image "assets/content/scenes/global/mirror/mirror.webp" "75%">>
<p class="mirror-section-label mirror-section-label--first">Routine</p>
<div class="location-actions">
<<if _hasRoutine>>
<<btn "Apply Routine" "mirrorApplyRoutine">><</btn>>
<</if>>
<<btn "Routine Settings" "mirrorRoutineSettingsEnter">><</btn>>
</div>
<p class="mirror-section-label">Body Care</p>
<div class="location-actions">
<<if $appearance.hairCombed === 1>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Already combed" data-tooltip-type="success"><i class="icon icon-lock icon-12"></i> Comb Hair</span>
<<else>>
<<activityButton "Comb Hair" "mirrorCombHair" "item:comb" false>>
<</if>>
<<if $daily.hairCreamUsed>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Already used today" data-tooltip-type="success"><i class="icon icon-lock icon-12"></i> Apply Hair Cream</span>
<<else>>
<<activityButton "Apply Hair Cream" "mirrorHairCream" "item:hair_cream" false>>
<</if>>
<<if $daily.faceCreamUsed>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Already used today" data-tooltip-type="success"><i class="icon icon-lock icon-12"></i> Face Care</span>
<<else>>
<<activityButton "Face Care" "mirrorFaceCare" "item:face_cream" false>>
<</if>>
<<if $daily.dentalCareUsed>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Already used today" data-tooltip-type="success"><i class="icon icon-lock icon-12"></i> Dental Care</span>
<<else>>
<<activityButton "Dental Care" "mirrorDentalCare" "item:toothpaste" false>>
<</if>>
</div>
<p class="mirror-section-label">Makeup</p>
<<if !_hasKit>>
<p class="mirror-hint">You need a makeup kit or portable makeup to apply a look.</p>
<</if>>
<div class="location-actions mirror-makeup-row-primary">
/* Light through Slutty (row 1) */
<<set _unlock1 = _hasKit && _corr >= 1>>
<<set _req1 = []>>
<<if _corr < 1>><<run _req1.push("corruption 1")>><</if>>
<<if !_hasKit>><<run _req1.push("makeup kit")>><</if>>
<<set _tip1 = "Need " + _req1.join(", ")>>
<<if _unlock1>><<btn "Light" "mirrorMakeupApply">><<set $makeupChosenStyle = 1>><</btn>><<else>><<btn "Light" "" "locked" _tip1>><</btn>><</if>>
<<set _unlock2 = _hasKit && _corr >= 3 && _skill >= 20>>
<<set _req2 = []>>
<<if _corr < 3>><<run _req2.push("corruption 3")>><</if>>
<<if _skill < 20>><<run _req2.push("makeup skill 20")>><</if>>
<<if !_hasKit>><<run _req2.push("makeup kit")>><</if>>
<<set _tip2 = "Need " + _req2.join(", ")>>
<<if _unlock2>><<btn "Medium" "mirrorMakeupApply">><<set $makeupChosenStyle = 2>><</btn>><<else>><<btn "Medium" "" "locked" _tip2>><</btn>><</if>>
<<set _unlock3 = _hasKit && _corr >= 4 && _skill >= 40>>
<<set _req3 = []>>
<<if _corr < 4>><<run _req3.push("corruption 4")>><</if>>
<<if _skill < 40>><<run _req3.push("makeup skill 40")>><</if>>
<<if !_hasKit>><<run _req3.push("makeup kit")>><</if>>
<<set _tip3 = "Need " + _req3.join(", ")>>
<<if _unlock3>><<btn "Heavy" "mirrorMakeupApply">><<set $makeupChosenStyle = 3>><</btn>><<else>><<btn "Heavy" "" "locked" _tip3>><</btn>><</if>>
<<set _unlock4 = _hasKit && _corr >= 5 && _skill >= 75>>
<<set _req4 = []>>
<<if _corr < 5>><<run _req4.push("corruption 5")>><</if>>
<<if _skill < 75>><<run _req4.push("makeup skill 75")>><</if>>
<<if !_hasKit>><<run _req4.push("makeup kit")>><</if>>
<<set _tip4 = "Need " + _req4.join(", ")>>
<<if _unlock4>><<btn "Slutty" "mirrorMakeupApply">><<set $makeupChosenStyle = 4>><</btn>><<else>><<btn "Slutty" "" "locked" _tip4>><</btn>><</if>>
<<set _unlock5 = _hasKit && _corr >= 5 && _skill >= 75>>
<<set _req5 = []>>
<<if _corr < 5>><<run _req5.push("corruption 5")>><</if>>
<<if _skill < 75>><<run _req5.push("makeup skill 75")>><</if>>
<<if !_hasKit>><<run _req5.push("makeup kit")>><</if>>
<<set _tip5 = "Need " + _req5.join(", ")>>
<<if _unlock5>><<btn "Bimbo" "mirrorMakeupApply">><<set $makeupChosenStyle = 5>><</btn>><<else>><<btn "Bimbo" "" "locked" _tip5>><</btn>><</if>>
<<if _makeupOn && _hasWipes>>
<<btn "Remove Makeup" "mirrorMakeupRemove" "secondary">><</btn>>
<<elseif _makeupOn>>
<span class="link-internal btn-style btn-default locked mirror-action-muted" data-tooltip="Need wet wipes to remove makeup"><i class="icon icon-lock icon-12"></i> Remove Makeup</span>
<<else>>
<span class="link-internal btn-style btn-default locked mirror-action-muted" data-tooltip="No makeup on" data-tooltip-type="success"><i class="icon icon-lock icon-12"></i> Remove Makeup</span>
<</if>>
</div>
<div class="location-actions">
<<btn "Back" _backTo "secondary">><</btn>>
</div>
<</nobr>>/* ==================================================
MIRROR WIDGETS
Redirect to Mirror passage and store return passage
================================================== */
/* Mirror button – goes to Mirror passage from anywhere; on click stores
current passage in $mirrorReturnPassage so Back in Mirror returns there.
Usage: <<mirrorBtn>> */
<<widget "mirrorBtn">>
<<btn "Mirror" "Mirror">>
<<set $mirrorReturnPassage = State.passage>>
<</btn>>
<</widget>>
<<nobr>>
/* ==========================================
MONEY SYSTEM WIDGETS
Handles cash and bank transactions
========================================== */
/* ==========================================
EARN CASH
Adds money to cash balance with optional notification
Usage: <<earnMoney 100>> or <<earnMoney 100 "optional message">>
========================================== */
<<widget "earnMoney">>
<<set _amount = $args[0]>>
<<set _message = $args[1] || null>>
<<if _amount > 0>>
<<set $moneyEarn += _amount>>
<<set $cashBalance = $moneyEarn - $moneySpend>>
<<if _message>>
<<run window.notifySuccess(_message)>>
<<elseif window.notifySuccess>>
<<run window.notifySuccess(`Money : +$${_amount}`)>>
<</if>>
<</if>>
<</widget>>
/* ==========================================
SPEND CASH
Deducts from cash balance if sufficient funds
Usage: <<spendCash 50>>
Returns: Sets _transactionSuccess to true/false
========================================== */
<<widget "spendCash">>
<<set _amount = $args[0]>>
<<set _transactionSuccess = false>>
<<if _amount > 0 && $cashBalance >= _amount>>
<<set $moneySpend += _amount>>
<<set $cashBalance = $moneyEarn - $moneySpend>>
<<set _transactionSuccess = true>>
<</if>>
<</widget>>
/* ==========================================
SPEND CARD
Deducts from bank balance if sufficient funds
Usage: <<spendCard 50>>
Returns: Sets _transactionSuccess to true/false
========================================== */
<<widget "spendCard">>
<<set _amount = $args[0]>>
<<set _transactionSuccess = false>>
<<if _amount > 0 && $bankBalance >= _amount>>
<<set $bankSpend += _amount>>
<<set $bankBalance = $bankDeposit - $bankSpend - $bankWithdraw>>
<<set _transactionSuccess = true>>
<</if>>
<</widget>>
/* ==========================================
DEPOSIT TO BANK
Transfers cash to bank account
Usage: <<depositBank 200>>
Returns: Sets _transactionSuccess to true/false
========================================== */
<<widget "depositBank">>
<<set _amount = $args[0]>>
<<set _transactionSuccess = false>>
<<if _amount > 0 && $cashBalance >= _amount>>
/* Deduct from cash */
<<set $moneySpend += _amount>>
<<set $cashBalance = $moneyEarn - $moneySpend>>
/* Add to bank */
<<set $bankDeposit += _amount>>
<<set $bankBalance = $bankDeposit - $bankSpend - $bankWithdraw>>
<<set _transactionSuccess = true>>
<</if>>
<</widget>>
/* ==========================================
WITHDRAW FROM BANK
Transfers bank funds to cash
Usage: <<withdrawBank 100>>
Returns: Sets _transactionSuccess to true/false
========================================== */
<<widget "withdrawBank">>
<<set _amount = $args[0]>>
<<set _transactionSuccess = false>>
<<if _amount > 0 && $bankBalance >= _amount>>
/* Deduct from bank */
<<set $bankWithdraw += _amount>>
<<set $bankBalance = $bankDeposit - $bankSpend - $bankWithdraw>>
/* Add to cash */
<<set $moneyEarn += _amount>>
<<set $cashBalance = $moneyEarn - $moneySpend>>
<<set _transactionSuccess = true>>
<</if>>
<</widget>>
/* ==========================================
CHECK BALANCE
Checks if player can afford amount
Usage: <<canAffordCash 50>> / <<canAffordCard 50>>
Returns: Sets _canAfford to true/false
========================================== */
<<widget "canAffordCash">>
<<set _canAfford = ($cashBalance >= $args[0])>>
<</widget>>
<<widget "canAffordCard">>
<<set _canAfford = ($bankBalance >= $args[0])>>
<</widget>>
<</nobr>>/* ==========================================
MOTHER TALK TOPICS LEVEL 1
IMG MAPPING:
kitchenMorning: coffee=1, breakfast=2, smalltalk=3
kitchenAfternoon: leftovers=1, chores=2, errand=3
kitchenEvening: dinner=1, family=2, help=3
livingroomMorning: news=1, laundry=2, newHouse=3
livingroomAfternoon: reading=1, tv=2, task=3
livingroomEvening: unwind=1, checkIn=2, endOfDay=3
backyardMorning: garden=1, coffee=2, quietMoment=3
backyardAfternoon: watering=1, resting=2, observation=3
parentsRoomMorning: gettingReady=1, lost=2, bedMaking=3
parentsRoomEvening: tired=1, goodnight=2, reflection=3
========================================== */
<<set setup.motherTopics = setup.motherTopics || {}>>
<<set setup.motherTopics.level1 = {
fhKitchen_morning: {
coffee: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>Mom's at the counter in her pajamas, staring at the coffee machine like it owes her something.<</narrative>>
<<dialog "mother">>Coffee's almost ready. You want some?<</dialog>>
<<dialog "player">>Please. You look exhausted.<</dialog>>
<<dialog "mother">>(yawning) I'll be fine once this kicks in. Didn't sleep great.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's pouring herself a second cup before she's even finished the first.<</narrative>>
<<dialog "mother">>Morning. Sleep okay?<</dialog>>
<<dialog "player">>Yeah, not bad. You?<</dialog>>
<<dialog "mother">>Passable. The house makes weird sounds at night. I keep waking up thinking someone left a tap on.<</dialog>>
<<dialog "player">>It's just the pipes. Old houses do that.<</dialog>>
<<dialog "mother">>(sighing) I know. Still unsettling.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's sitting at the table, both hands wrapped around her mug, staring at nothing in particular.<</narrative>>
<<dialog "mother">>You're up early.<</dialog>>
<<dialog "player">>Couldn't really sleep past seven.<</dialog>>
<<dialog "mother">>That'll change. Takes a bit to adjust to a new place. Grab a mug if you want coffee's there.<</dialog>>`,
friendship: 1
}
],
breakfast: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's at the stove making eggs, spatula in hand, moving with the ease of someone who's done this ten thousand times.<</narrative>>
<<dialog "mother">>Eggs?<</dialog>>
<<dialog "player">>Sure, please.<</dialog>>
<<dialog "mother">>(cracking another one) I was thinking of going to the market today. You need anything?<</dialog>>
<<dialog "player">>I'll try to think of something.<</dialog>>
<<dialog "mother">>Write it down or you'll forget. I always forget.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's slicing bread, the toaster already golden and warm on the counter.<</narrative>>
<<dialog "mother">>Toast is ready. There's butter and jam if you want it.<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "mother">>Do you eat breakfast normally? Back home you used to skip it.<</dialog>>
<<dialog "player">>Sometimes. I'm trying to be better about it.<</dialog>>
<<dialog "mother">>(approvingly) Good. It makes a difference.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's eating standing up, plate balanced in one hand, scrolling through her phone with the other.<</narrative>>
<<dialog "mother">>Oh there's oatmeal on the stove if you want some.<</dialog>>
<<dialog "player">>You're eating standing again.<</dialog>>
<<dialog "mother">>(looking down at herself) Hm. Habit.<</dialog>>
<<dialog "player">>Sit with me.<</dialog>>
<<dialog "mother">>(pausing, then moving to the table) Alright. Just for a minute.<</dialog>>`,
friendship: 2
}
],
smalltalk: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's reading a recipe on her tablet, propped against the fruit bowl.<</narrative>>
<<dialog "mother">>Is it cold in your room? I keep meaning to check the heating.<</dialog>>
<<dialog "player">>It's fine, honestly.<</dialog>>
<<dialog "mother">>We're not made of money, but we're also not living in a freezer. Tell me if it gets worse.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She glances out the window. The sky's grey.<</narrative>>
<<dialog "mother">>Looks like rain. Do you have plans today?<</dialog>>
<<dialog "player">>Not really. Maybe go out later if it clears.<</dialog>>
<<dialog "mother">>Take an umbrella just in case. Third drawer.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's rinsing dishes, apparently working through something in her head.<</narrative>>
<<dialog "mother">>Your father said the garage door makes a noise. You haven't heard anything, have you?<</dialog>>
<<dialog "player">>I haven't really been out that way.<</dialog>>
<<dialog "mother">>I'll get someone to look at it. These things don't fix themselves.
(glancing at you)
I hate calling repairmen. They always act like you should already know what's wrong.<</dialog>>`,
friendship: 1, trust: 1
}
]
},
fhKitchen_afternoon: {
leftovers: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She's putting food away in containers, labeling them with little sticky notes.<</narrative>>
<<dialog "mother">>Did you eat lunch? There's leftovers in the fridge. Top shelf.<</dialog>>
<<dialog "player">>I grabbed something earlier.<</dialog>>
<<dialog "mother">>(not quite convinced) Something as in real food, or something as in crackers?<</dialog>>
<<dialog "player">>...Both.<</dialog>>
<<dialog "mother">>There's soup. Heat it up properly. Not just lukewarm. Actually hot.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She's eating alone at the table, the remains of a simple lunch in front of her.<</narrative>>
<<dialog "mother">>You're just in time. Sit.<</dialog>>
<<dialog "player">>I don't want to interrupt.<</dialog>>
<<dialog "mother">>You're not interrupting. I was just about to clear up anyway. There's enough for two.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She's wrapping something in foil, frowning at the fridge like she's doing mental math.<</narrative>>
<<dialog "mother">>I made too much again. I always make too much.<</dialog>>
<<dialog "player">>We'll eat it tomorrow.<</dialog>>
<<dialog "mother">>(dryly) That's what I said yesterday. Someone's going to have to actually eat this.<</dialog>>`,
friendship: 1
}
],
chores: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>She's wiping down the counter with focused, rhythmic strokes.<</narrative>>
<<dialog "mother">>(without looking up) You didn't leave the bathroom light on again, did you?<</dialog>>
<<dialog "player">>I don't think so.<</dialog>>
<<dialog "mother">>I keep finding it on. We're not made of electricity.<</dialog>>
<<dialog "player">>I'll be more careful.<</dialog>>
<<dialog "mother">>(looking up, softer) That's all I'm asking.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>She's reorganizing the pantry shelf, things shuffled in and out of place.<</narrative>>
<<dialog "mother">>Can you hold this for a second?<</dialog>>
<<dialog "player">>(grabbing the box) What are you doing?<</dialog>>
<<dialog "mother">>This shelf wobbles. I keep meaning to fix it. Your father keeps saying he will and then he doesn't.<</dialog>>
<<dialog "player">>I could take a look at it.<</dialog>>
<<dialog "mother">>(genuinely) Could you? That would actually be helpful.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>She's going through the recycling, sorting with quiet efficiency.<</narrative>>
<<dialog "mother">>We go through a lot of packaging. I'm trying to buy less of it but it's everywhere.<</dialog>>
<<dialog "player">>It's nearly impossible to avoid.<</dialog>>
<<dialog "mother">>I know. Still.
(holding up a plastic bag)
Would you mind taking this to the bin outside?<</dialog>>
<<dialog "player">>Sure.<</dialog>>`,
friendship: 1
}
],
errand: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's making tea, pulling out a second mug without being asked.<</narrative>>
<<dialog "mother">>I went to the market this morning. They had good biscuits. Blue tin, top cupboard.<</dialog>>
<<dialog "player">>Nice.<</dialog>>
<<dialog "mother">>Don't eat them all at once. They're supposed to last the week.<</dialog>>
<<dialog "player">>No promises.<</dialog>>
<<dialog "mother">>(a small smile) I figured as much.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's checking her phone against a handwritten shopping list, crossing things off.<</narrative>>
<<dialog "mother">>I need to go out again later. Did you think of anything you need?<</dialog>>
<<dialog "player">>Not really. Maybe face wash?<</dialog>>
<<dialog "mother">>(writing it down) What kind?<</dialog>>
<<dialog "player">>Any kind is fine.<</dialog>>
<<dialog "mother">>(writing) I'll get the one I saw last time. You can tell me if it's wrong.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's putting away groceries, filling up the fridge methodically.<</narrative>>
<<dialog "mother">>Your father wanted that cereal again. I got two boxes because he'll go through one in four days.<</dialog>>
<<dialog "player">>He does love his cereal.<</dialog>>
<<dialog "mother">>(flatly) At his age. Baffling.
(stacking something)
I got you a few extra things too. Just basics.<</dialog>>
<<dialog "player">>You didn't have to do that.<</dialog>>
<<dialog "mother">>I know.<</dialog>>`,
friendship: 2
}
]
},
fhKitchen_evening: {
dinner: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>She's stirring something on the stove, steam rising. The kitchen smells warm and familiar.<</narrative>>
<<dialog "mother">>Perfect timing. Can you set the table?<</dialog>>
<<dialog "player">>Sure.<</dialog>>
<<dialog "mother">>Forks on the left. Your father always gets it wrong.<</dialog>>
<<dialog "player">>(amused) I know, I know.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>She's tasting the sauce off a wooden spoon, brow furrowed in concentration.<</narrative>>
<<dialog "mother">>Here taste this. Tell me if it needs something.<</dialog>>
<<dialog "player">>(tasting) Maybe a bit more salt?<</dialog>>
<<dialog "mother">>(adding a pinch) You've always had a good palate. Must get it from my side.<</dialog>>
<<dialog "player">>(smiling) Must.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-1.webp">>
<<image _img "100%">>
<<narrative>>She's draining pasta into the sink, a cloud of steam rising up around her.<</narrative>>
<<dialog "mother">>Can you grab the colander? Bottom left cabinet.<</dialog>>
<<dialog "player">>(finding it) Here.<</dialog>>
<<dialog "mother">>Thank you.
(starting to drain)
About twenty minutes. You hungry?<</dialog>>
<<dialog "player">>Yeah, actually.<</dialog>>
<<dialog "mother">>Good. Go wash up.<</dialog>>`,
friendship: 1
}
],
family: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>She's setting plates out, glancing at the clock.<</narrative>>
<<dialog "mother">>Your father called. He'll be a bit late again.<</dialog>>
<<dialog "player">>Again?<</dialog>>
<<dialog "mother">>(evenly) New job. It's an adjustment. It'll settle down.
(to herself)
It always settles down.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>She's putting together a salad, still dressed from earlier.<</narrative>>
<<dialog "mother">>What did you do today?<</dialog>>
<<dialog "player">>Not much. Settled in a bit more.<</dialog>>
<<dialog "mother">>(looking up) That's fine. You don't have to fill every day with things right away.<</dialog>>
<<dialog "player">>I know. Sometimes it just feels like I should.<</dialog>>
<<dialog "mother">>That feeling goes away. Give it time.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-2.webp">>
<<image _img "100%">>
<<narrative>>Jake wanders in and grabs something from the fridge, disappears again. Mom watches him go.<</narrative>>
<<dialog "mother">>(quietly) Does he seem okay to you?<</dialog>>
<<dialog "player">>Jake? Yeah, I think so. Why?<</dialog>>
<<dialog "mother">>He's been in his room a lot. Adjusting to the new school, I suppose.
(stirring)
Keep an eye on him if you can. He listens to you.<</dialog>>`,
friendship: 2, trust: 2
}
],
help: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's got the oven going, the stove going, and something in the sink.<</narrative>>
<<dialog "mother">>Could you watch the pot? Just make sure it doesn't boil over. I'll be right back.<</dialog>>
<<dialog "player">>Got it.<</dialog>>
<<dialog "mother">>If it starts bubbling hard, just turn it down a little. Don't touch anything else.<</dialog>>
<<dialog "player">>Just the one knob. I can manage.<</dialog>>
<<dialog "mother">>(already heading out) I know. Thank you.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's washing up while things cook, hands deep in soapy water.<</narrative>>
<<dialog "mother">>Can you dry?<</dialog>>
<<dialog "player">>(picking up the towel) Sure.<</dialog>>
<<dialog "mother">>Careful with the big pan. The handle gets hot even after it's been washed.<</dialog>>
<<dialog "player">>Noted.<</dialog>>
<<dialog "mother">>Your grandmother did this every night. Wash and dry together. I don't know why I keep doing it.<</dialog>>
<<dialog "player">>Habit, probably.<</dialog>>
<<dialog "mother">>(considering) Yes. I suppose.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/kitchenEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's slicing bread at the counter, clearly tired but pushing through.<</narrative>>
<<dialog "mother">>Could you check if there's more olive oil? The cupboard above the stove.<</dialog>>
<<dialog "player">>(checking) There's one, almost full.<</dialog>>
<<dialog "mother">>Good. I thought I used it up.
(exhaling)
Small victories.<</dialog>>`,
friendship: 1
}
]
},
fhLivingroom_morning: {
news: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's on the couch with her coffee, watching the morning news with a vaguely worried expression.<</narrative>>
<<dialog "mother">>Morning. Do you need the TV?<</dialog>>
<<dialog "player">>No, I'm just passing through.<</dialog>>
<<dialog "mother">>(gesturing at the screen) They've been talking about the same story for twenty minutes. I can't tell if it's actually important or just slow news day.<</dialog>>
<<dialog "player">>Probably both.<</dialog>>
<<dialog "mother">>(dryly) That tracks.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She mutes the TV when you walk in, like she's been waiting for someone to talk to.<</narrative>>
<<dialog "mother">>Apparently it's going to rain all week.<</dialog>>
<<dialog "player">>Of course it is.<</dialog>>
<<dialog "mother">>(sighing) I had plans for the garden. They'll have to wait.
(picking up her mug)
Do you have anything going on today?<</dialog>>
<<dialog "player">>Not really. We'll see.<</dialog>>
<<dialog "mother">>(nodding) Some days are like that.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's watching something but clearly not really following it anymore.<</narrative>>
<<dialog "mother">>Come sit a minute.<</dialog>>
<<dialog "player">>(sitting) What are you watching?<</dialog>>
<<dialog "mother">>Honestly no idea. I turned it on for company.
(glancing at you)
How are you settling in? Really.<</dialog>>
<<dialog "player">>Getting there. It just takes time.<</dialog>>
<<dialog "mother">>(nodding slowly) It does.<</dialog>>`,
friendship: 1, trust: 1
}
],
laundry: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's folding laundry on the couch, working through the pile with quiet efficiency.<</narrative>>
<<dialog "mother">>Oh good, you're here. Can you hand me those towels?<</dialog>>
<<dialog "player">>(handing them over) You know I can help with this, right?<</dialog>>
<<dialog "mother">>I know. I just like doing it while it's quiet.
(folding quickly)
Your shirts are in that pile if you want them.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's matched all the socks and is now holding up one that has no pair.<</narrative>>
<<dialog "mother">>Do you have odd socks upstairs? Because I've got one with no match and it's bothering me.<</dialog>>
<<dialog "player">>(thinking) Possibly.<</dialog>>
<<dialog "mother">>It's always one. Never zero or two. Exactly one.<</dialog>>
<<dialog "player">>That's just how it works.<</dialog>>
<<dialog "mother">>(staring at the sock) Infuriating.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's just finished a big pile, things neatly stacked in separate rows.<</narrative>>
<<dialog "mother">>Can you take these up? The top two piles are yours.<</dialog>>
<<dialog "player">>(picking them up) Sure. Want me to put them away?<</dialog>>
<<dialog "mother">>Just leave them on the bed. I'll sort them later.
(looking at the remaining pile)
Actually, yes. Put them away or it'll just sit there.<</dialog>>`,
friendship: 1
}
],
newHouse: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's looking out the window at the street, coffee in hand, quiet.<</narrative>>
<<dialog "mother">>(without turning) The house makes sounds at night. Have you noticed?<</dialog>>
<<dialog "player">>Yeah, a bit.<</dialog>>
<<dialog "mother">>It's just settling. Old houses do that.
(turning)
Still. I keep waking up thinking someone's there.<</dialog>>
<<dialog "player">>You'll get used to it. We all will.<</dialog>>
<<dialog "mother">>(small smile) I know.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's looking at the room critically, like she's still mentally rearranging it.<</narrative>>
<<dialog "mother">>I think the sofa should be over there. More light in the mornings.<</dialog>>
<<dialog "player">>Want to move it?<</dialog>>
<<dialog "mother">>Not right now. I just like to think about it.
(sitting back down)
Does the room feel small to you?<</dialog>>
<<dialog "player">>No, I think it's fine.<</dialog>>
<<dialog "mother">>(satisfied) Good. I worried it was smaller than the old one.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's putting up a framed photo on the wall. Adjusting it, stepping back, adjusting again.<</narrative>>
<<dialog "mother">>Is this level?<</dialog>>
<<dialog "player">>(looking) Almost. A tiny bit to the left.<</dialog>>
<<dialog "mother">>(adjusting) Here?<</dialog>>
<<dialog "player">>Perfect.<</dialog>>
<<dialog "mother">>(stepping back, satisfied) Good. It was bothering me every morning.<</dialog>>`,
friendship: 2
}
]
},
fhLivingroom_afternoon: {
reading: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She's on the couch with a book, legs tucked under her. She dog-ears the page when she sees you.<</narrative>>
<<dialog "mother">>Hey. What are you up to?<</dialog>>
<<dialog "player">>Not much. What are you reading?<</dialog>>
<<dialog "mother">>A book that I can't decide if I like.
(showing the cover)
People making bad decisions. Very relatable. Possibly too relatable.<</dialog>>
<<dialog "player">>(sitting down) Sounds compelling.<</dialog>>
<<dialog "mother">>That's the annoying part. It is.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She's fallen asleep with her book open on her chest. She wakes up when you come in, a bit embarrassed.<</narrative>>
<<dialog "mother">>(startled) Oh I wasn't asleep.<</dialog>>
<<dialog "player">>(kindly) Sure.<</dialog>>
<<dialog "mother">>(straightening up) The book's very boring. It's the book's fault.<</dialog>>
<<dialog "player">>Absolutely.<</dialog>>
<<dialog "mother">>(smoothing her hair) Don't tell your father.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She's finished her book and is sitting with it closed in her lap, looking vaguely satisfied.<</narrative>>
<<dialog "mother">>I finished it.<</dialog>>
<<dialog "player">>The book?<</dialog>>
<<dialog "mother">>Mm. I'm not sure how I feel about the ending. I need a minute with it.
(looking at you)
Do you read much?<</dialog>>
<<dialog "player">>Sometimes. Depends on the book.<</dialog>>
<<dialog "mother">>That's the right answer.<</dialog>>`,
friendship: 1
}
],
tv: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>She's watching something on TV but doesn't seem that into it. She turns the volume down when you come in.<</narrative>>
<<dialog "mother">>How was your morning? Do anything interesting?<</dialog>>
<<dialog "player">>Not really. Quiet day.<</dialog>>
<<dialog "mother">>(nodding) Those are good sometimes. I should have more of them.
(glancing back at the screen)
This show is terrible. I can't stop watching it.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>She pats the seat next to her.<</narrative>>
<<dialog "mother">>Come sit. I need someone to be annoyed at this with me.<</dialog>>
<<dialog "player">>(sitting) What's happening?<</dialog>>
<<dialog "mother">>This character is making the worst decision possible and everyone around her is just watching.
(gesturing at the screen)
Say something!<</dialog>>
<<dialog "player">>(laughing) It's fiction, they can't hear you.<</dialog>>
<<dialog "mother">>That has never stopped me.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>The TV's off. She's just sitting quietly with her tea.<</narrative>>
<<dialog "mother">>I turned it off. Too much noise.<</dialog>>
<<dialog "player">>Peaceful.<</dialog>>
<<dialog "mother">>(agreeing) Mm. I forget how loud the default is sometimes.
(looking at you)
Are you doing okay? You seem a bit in your head.<</dialog>>
<<dialog "player">>Just thinking.<</dialog>>
<<dialog "mother">>(simply) That's okay too.<</dialog>>`,
friendship: 1, trust: 1
}
],
task: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's writing in a small notebook. Lists, by the look of it.<</narrative>>
<<dialog "mother">>I'm making a list of things that still need doing in the house. The shelf, the hallway light, that gap in the kitchen skirting board...<</dialog>>
<<dialog "player">>I didn't know about the gap.<</dialog>>
<<dialog "mother">>(showing you) Back corner. It's been bothering me since we moved in. I keep stepping around it mentally.<</dialog>>
<<dialog "player">>I could have a look at that.<</dialog>>
<<dialog "mother">>(looking up) Really? That would help.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's dusting the bookshelves, methodical about it.<</narrative>>
<<dialog "mother">>The dust collects so fast in a new house. No idea why.<</dialog>>
<<dialog "player">>Something about the settling process probably.<</dialog>>
<<dialog "mother">>(wiping) Could be.
(glancing at you)
Can you move that lamp while I get back here?<</dialog>>
<<dialog "player">>(moving it) Sure. Here?<</dialog>>
<<dialog "mother">>Perfect.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's trying to hang something on the wall, struggling slightly with the angle.<</narrative>>
<<dialog "mother">>Hold this flat? I need both hands for the drill.<</dialog>>
<<dialog "player">>(holding the frame) Are you sure about the height?<</dialog>>
<<dialog "mother">>No. But if it's wrong, we fix it.
(drilling carefully)
Life philosophy.<</dialog>>`,
friendship: 2
}
]
},
fhLivingroom_evening: {
unwind: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>The living room is lit warmly, the TV on low. She's settled into her usual corner of the couch.<</narrative>>
<<dialog "mother">>Come watch something with me. I can't decide what to put on.<</dialog>>
<<dialog "player">>What are you in the mood for?<</dialog>>
<<dialog "mother">>Something that doesn't require thinking. I've done enough of that today.<</dialog>>
<<dialog "player">>(sitting beside her) I know a few options.<</dialog>>
<<dialog "mother">>You pick. I trust you.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>She's putting away odds and ends around the room, tidying without really thinking about it.<</narrative>>
<<dialog "mother">>Can you believe the day went that fast?<</dialog>>
<<dialog "player">>It always does.<</dialog>>
<<dialog "mother">>(sighing) I had a whole list. I did maybe two things on it.<</dialog>>
<<dialog "player">>Two is something.<</dialog>>
<<dialog "mother">>(smiling tiredly) That's very generous of you.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>She's on the couch with herbal tea, the house quieter than usual.<</narrative>>
<<dialog "mother">>Do you want tea? I just made some.<</dialog>>
<<dialog "player">>Sure.<</dialog>>
<<dialog "mother">>(getting up) It's the chamomile one. It helps me wind down. I've been having it every night since we moved in.<</dialog>>
<<dialog "player">>Good habit.<</dialog>>
<<dialog "mother">>(from the kitchen) There are worse ones.<</dialog>>`,
friendship: 1
}
],
checkIn: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>Dad's watching sports in the background. She's sitting with her phone, half-watching.<</narrative>>
<<dialog "mother">>(quietly) How are you doing? Really.<</dialog>>
<<dialog "player">>I'm good. Why?<</dialog>>
<<dialog "mother">>Just checking. You've been a bit quiet lately.<</dialog>>
<<dialog "player">>Just adjusting, I think.<</dialog>>
<<dialog "mother">>(nodding) That's normal. We all are. Give it time.<</dialog>>`,
friendship: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>She's alone in the room for once, the house quiet around her.<</narrative>>
<<dialog "mother">>There you are. Sit for a minute?<</dialog>>
<<dialog "player">>(sitting) Everything okay?<</dialog>>
<<dialog "mother">>Yes, just... I wanted to check in. We've all been so busy adjusting, I feel like I haven't actually talked to anyone in days.<</dialog>>
<<dialog "player">>I know what you mean.<</dialog>>
<<dialog "mother">>Good. Then just sit with me for a bit. That's all.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>She notices you standing in the doorway and waves you in.<</narrative>>
<<dialog "mother">>Come in, don't hover. What's on your mind?<</dialog>>
<<dialog "player">>Nothing specific. Just restless.<</dialog>>
<<dialog "mother">>Sit then. Restless is fine. You don't always need a reason to be somewhere.<</dialog>>`,
friendship: 1
}
],
endOfDay: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's turning off the side lamps, getting ready to head up for the night.<</narrative>>
<<dialog "mother">>Going up soon. Don't stay up too late, okay?<</dialog>>
<<dialog "player">>I won't.<</dialog>>
<<dialog "mother">>(not entirely convinced) Mm. Goodnight, then.<</dialog>>
<<dialog "player">>Night, Mom.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's about to head up but pauses when you come in.<</narrative>>
<<dialog "mother">>Oh good, I wanted to ask. Did you lock the back door?<</dialog>>
<<dialog "player">>I think so. I'll check.<</dialog>>
<<dialog "mother">>Thank you. Your father always forgets.
(heading toward the stairs)
Goodnight. Sleep well.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/livingroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's collecting mugs and glasses from around the room, doing a last round before bed.<</narrative>>
<<dialog "mother">>Can you grab that one? On the far side.<</dialog>>
<<dialog "player">>(handing it over) Here.<</dialog>>
<<dialog "mother">>Thank you. Okay. I think that's everything.
(looking around one last time)
Another day done.<</dialog>>
<<dialog "player">>Another day done.<</dialog>>
<<dialog "mother">>(quietly) Goodnight.<</dialog>>`,
friendship: 2
}
]
},
fhBackyard_morning: {
garden: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's standing with her coffee, studying the garden with the focused expression of someone who sees potential others don't.<</narrative>>
<<dialog "mother">>I want to plant something along that wall. Something that comes back every year.<</dialog>>
<<dialog "player">>Perennials?<</dialog>>
<<dialog "mother">>(surprised) Yes, exactly. Do you garden?<</dialog>>
<<dialog "player">>Not really. I just know the word.<</dialog>>
<<dialog "mother">>(smiling) That's a start.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's crouched by one of the beds, pulling weeds with focused patience.<</narrative>>
<<dialog "mother">>(looking up) There you are. Hand me that trowel?<</dialog>>
<<dialog "player">>(handing it over) What are you planting?<</dialog>>
<<dialog "mother">>Nothing yet. Just clearing. You have to make room before you can grow anything.<</dialog>>
<<dialog "player">>Makes sense.<</dialog>>
<<dialog "mother">>(back to working) Most things do, once you think about them.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's examining a small potted plant that doesn't look great, turning it slowly.<</narrative>>
<<dialog "mother">>I think I over-watered this one. Or under-watered. I can never tell which.<</dialog>>
<<dialog "player">>How do you tell?<</dialog>>
<<dialog "mother">>You feel the soil. Dry means water, soggy means stop.
(pressing a finger in)
This one's... undecided.<</dialog>>
<<dialog "player">>(looking) Could be a new variety. Confused plant.<</dialog>>
<<dialog "mother">>(laughing softly) That's not comforting.<</dialog>>`,
friendship: 2
}
],
coffee: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's sitting in the garden chair, second coffee of the morning, looking more awake than earlier.<</narrative>>
<<dialog "mother">>You want to sit a minute?<</dialog>>
<<dialog "player">>Sure.<</dialog>>
<<dialog "mother">>(looking at the yard) I love mornings out here. Before the day takes over.<</dialog>>
<<dialog "player">>It's peaceful.<</dialog>>
<<dialog "mother">>(quietly) Yes. It is.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She offers you a mug without you asking. She must have brought two out.<</narrative>>
<<dialog "mother">>I thought you might come out. It's a nice morning.<</dialog>>
<<dialog "player">>(taking the mug) Thanks.<</dialog>>
<<dialog "mother">>The light is good today. I keep meaning to take photos of the garden but then I don't.<</dialog>>
<<dialog "player">>For what?<</dialog>>
<<dialog "mother">>Just to remember it. How things look before they change.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's leaning against the door frame with her mug, looking out at the yard without going in yet.<</narrative>>
<<dialog "mother">>Do you hear that?<</dialog>>
<<dialog "player">>(listening) Birds?<</dialog>>
<<dialog "mother">>There's a pair of them in the back tree. I've seen them every morning this week.
(sipping her coffee)
I think they might be nesting.<</dialog>>
<<dialog "player">>That's nice.<</dialog>>
<<dialog "mother">>It is, actually.<</dialog>>`,
friendship: 1
}
],
quietMoment: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's just standing in the middle of the backyard, doing nothing. You don't see that much.<</narrative>>
<<dialog "player">>You okay?<</dialog>>
<<dialog "mother">>(coming back to herself) Yes. Sorry. I was just... being here for a minute.<</dialog>>
<<dialog "player">>Nothing wrong with that.<</dialog>>
<<dialog "mother">>(lightly) No. There isn't.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's sitting perfectly still, eyes closed, face tilted toward the sun.<</narrative>>
<<dialog "player">>(quietly) Should I come back?<</dialog>>
<<dialog "mother">>(opening one eye) No, it's fine. I'm just storing up sunlight. For winter.<</dialog>>
<<dialog "player">>(smiling) Does that work?<</dialog>>
<<dialog "mother">>Probably not. But it feels like it might.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's sitting on the back step, not doing anything in particular, just being outside.<</narrative>>
<<dialog "mother">>Come sit. I don't have anything to say. I just wanted company.<</dialog>>
<<dialog "player">>(sitting beside her) I can do that.<</dialog>>
<<dialog "mother">>Good.
(a comfortable pause)
It's a nice morning.<</dialog>>
<<dialog "player">>Yeah. It is.<</dialog>>`,
friendship: 2, trust: 1
}
]
},
fhBackyard_afternoon: {
watering: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She's moving through the garden with the watering can, methodical, unhurried.<</narrative>>
<<dialog "mother">>These need more water than I thought. The soil here drains faster than the old house.<</dialog>>
<<dialog "player">>How can you tell?<</dialog>>
<<dialog "mother">>You learn to read it. It's drier than it looks on top.
(glancing at you)
Do you garden at all?<</dialog>>
<<dialog "player">>Not really. Never had a reason to.<</dialog>>
<<dialog "mother">>It's easier than it looks. Patience and water, mostly.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She hands you the watering can without asking.<</narrative>>
<<dialog "mother">>Start on the left side? I'll do the pots.<</dialog>>
<<dialog "player">>(taking it) Sure. How much?<</dialog>>
<<dialog "mother">>Until water comes out the bottom, then stop.<</dialog>>
<<dialog "player">>Easy enough.<</dialog>>
<<dialog "mother">>(moving to the pots) That's all gardening is, really. Pay attention and don't overdo it.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>She's refilling the watering can at the outdoor tap, waiting for it to fill.<</narrative>>
<<dialog "mother">>I keep thinking I should get a bigger can. This one takes five trips.<</dialog>>
<<dialog "player">>I could bring out a bucket.<</dialog>>
<<dialog "mother">>(considering) That might actually work.
(looking at you)
You're surprisingly practical.<</dialog>>
<<dialog "player">>Occasionally.<</dialog>>`,
friendship: 1
}
],
resting: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>She's sitting in the shade with a book and a glass of water. She shifts to make room when she sees you.<</narrative>>
<<dialog "mother">>Come sit. I could use the company.<</dialog>>
<<dialog "player">>(sitting) What are you reading?<</dialog>>
<<dialog "mother">>(showing the cover) Garden design. Very exciting stuff. I fell asleep twice already.<</dialog>>
<<dialog "player">>(laughing) Good nap material.<</dialog>>
<<dialog "mother">>The best kind.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>She's doing nothing, just sitting in the garden chair, face angled toward the light.<</narrative>>
<<dialog "mother">>Don't have anywhere to be, do you?<</dialog>>
<<dialog "player">>Not right now.<</dialog>>
<<dialog "mother">>Good. Sit for a bit then. It's nice out.<</dialog>>
<<dialog "player">>(sitting) It's warm for this time of year.<</dialog>>
<<dialog "mother">>Mm. I'm not complaining.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>She's put her book down and is just watching a bee work its way through the flowers.<</narrative>>
<<dialog "mother">>(quietly) Look at that.<</dialog>>
<<dialog "player">>(looking) The bee?<</dialog>>
<<dialog "mother">>It's been going back and forth for ten minutes. Very committed.<</dialog>>
<<dialog "player">>Unlike some of us.<</dialog>>
<<dialog "mother">>(amused) Speak for yourself.<</dialog>>`,
friendship: 2
}
],
observation: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's deadheading the flowers, working slowly and carefully.<</narrative>>
<<dialog "mother">>You have to cut these back or they stop blooming. Sounds harsh but it's better.<</dialog>>
<<dialog "player">>There's probably a life lesson in there somewhere.<</dialog>>
<<dialog "mother">>(pause, then) Probably.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's looking at the neighboring yard over the fence, thoughtful.<</narrative>>
<<dialog "mother">>The neighbors have lavender. Lots of it.<</dialog>>
<<dialog "player">>Smells good.<</dialog>>
<<dialog "mother">>I want some. I've always wanted lavender but never had the right spot for it.
(looking at her own garden)
Maybe over there.<</dialog>>
<<dialog "player">>I think it would work.<</dialog>>
<<dialog "mother">>(quietly pleased) Maybe.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/backyardAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>She's looking at the corner of the yard. It doesn't get much sun.<</narrative>>
<<dialog "mother">>What do you think goes in a shady corner?<</dialog>>
<<dialog "player">>Ferns? Hostas?<</dialog>>
<<dialog "mother">>(nodding slowly) Ferns. Yes. I hadn't thought of ferns.
(looking at you)
You do know gardening.<</dialog>>
<<dialog "player">>I know words. It's different.<</dialog>>
<<dialog "mother">>(smiling) Sometimes that's enough to start with.<</dialog>>`,
friendship: 2
}
]
},
fhParentsRoom_morning: {
gettingReady: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's at the mirror, brushing her hair quickly. She sees you in the reflection.<</narrative>>
<<dialog "mother">>Come in, it's fine. What do you need?<</dialog>>
<<dialog "player">>Nothing. Just looking for you.<</dialog>>
<<dialog "mother">>(still brushing) I'm almost done. Two minutes.<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's choosing between two similar-looking blouses, holding them up in turn.<</narrative>>
<<dialog "mother">>Which one?<</dialog>>
<<dialog "player">>(looking) The left one.<</dialog>>
<<dialog "mother">>Really?<</dialog>>
<<dialog "player">>You asked.<</dialog>>
<<dialog "mother">>(hanging the other one back) Fine. Left one.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-1.webp">>
<<image _img "100%">>
<<narrative>>She's making the bed, smoothing the duvet with practiced precision.<</narrative>>
<<dialog "mother">>(without stopping) Good morning. Sleep well?<</dialog>>
<<dialog "player">>Not bad.<</dialog>>
<<dialog "mother">>Good.
(finishing)
I don't know why but an unmade bed makes my whole morning feel wrong.<</dialog>>
<<dialog "player">>I should probably make mine more.<</dialog>>
<<dialog "mother">>(looking at you) You should. It helps. Trust me.<</dialog>>`,
friendship: 1, trust: 1
}
],
lost: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's checking nightstands and dresser drawers, clearly looking for something.<</narrative>>
<<dialog "mother">>Have you seen my reading glasses? Small, brown frames.<</dialog>>
<<dialog "player">>I saw them on the kitchen counter last night.<</dialog>>
<<dialog "mother">>(relieved) Of course. Thank you. I'd lose my head if it wasn't attached.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's hunting through her bag, increasingly frustrated.<</narrative>>
<<dialog "mother">>I cannot find my oh. There it is.
(pulling out her phone)
I was looking for this for ten minutes.<</dialog>>
<<dialog "player">>It was in your bag the whole time?<</dialog>>
<<dialog "mother">>(flatly) Apparently. Don't say it.<</dialog>>
<<dialog "player">>Say what?<</dialog>>
<<dialog "mother">>Whatever you're thinking.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-2.webp">>
<<image _img "100%">>
<<narrative>>She's holding a key that she clearly doesn't recognize.<</narrative>>
<<dialog "mother">>Do you know what this opens? I found it in the drawer.<</dialog>>
<<dialog "player">>(looking) No idea. Old key?<</dialog>>
<<dialog "mother">>(staring at it) It's been in my purse for years. I've moved it through three houses. I should probably throw it away.
(pausing)
I won't, though.<</dialog>>`,
friendship: 1
}
],
bedMaking: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's making the bed and hands you the other side of the duvet without asking.<</narrative>>
<<dialog "mother">>Grab that end?<</dialog>>
<<dialog "player">>(taking it) Sure.<</dialog>>
<<dialog "mother">>Pull it even. There.<</dialog>>
<<dialog "player">>You're very precise about this.<</dialog>>
<<dialog "mother">>Thirty years of habit.
(smoothing her side)
Thank you.<</dialog>>`,
friendship: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's arranging the pillows just so, repositioning them twice.<</narrative>>
<<dialog "mother">>Do you make your bed in the morning?<</dialog>>
<<dialog "player">>...Sometimes.<</dialog>>
<<dialog "mother">>(giving you a look) Make it. Every morning, first thing. Even if nothing else goes right, at least that's done.<</dialog>>
<<dialog "player">>That's oddly wise.<</dialog>>
<<dialog "mother">>I've been saying it for years. Your father still doesn't listen.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomMorning-3.webp">>
<<image _img "100%">>
<<narrative>>She's changing the pillowcases, working briskly.<</narrative>>
<<dialog "mother">>Fresh sheets day. Best day of the week.<</dialog>>
<<dialog "player">>Fresh sheets day?<</dialog>>
<<dialog "mother">>Monday. Always Monday.
(snapping a pillowcase)
Some things have to stay the same when everything else changes.<</dialog>>`,
friendship: 1
}
]
},
fhParentsRoom_evening: {
tired: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>She's sitting on the edge of the bed, shoes off, massaging one foot absently.<</narrative>>
<<dialog "mother">>Come in. Long day?<</dialog>>
<<dialog "player">>Should ask you that.<</dialog>>
<<dialog "mother">>(shrugging) My feet think so. I keep forgetting that being home all day is still being on your feet all day.<</dialog>>
<<dialog "player">>Sit for once. You don't have to keep moving.<</dialog>>
<<dialog "mother">>(looking at you) I know. I'm trying.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>She's sitting at the small vanity, taking off jewelry slowly, like she's finally decompressing.<</narrative>>
<<dialog "mother">>(without turning) Is everyone downstairs okay?<</dialog>>
<<dialog "player">>Yeah, all fine. You okay up here?<</dialog>>
<<dialog "mother">>Just tired. Good tired, I think. Not the bad kind.
(setting down an earring)
Is there anything you need?<</dialog>>
<<dialog "player">>No. Just wanted to check in.<</dialog>>
<<dialog "mother">>(softer) That's nice. Thank you.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-1.webp">>
<<image _img "100%">>
<<narrative>>She's already in pajamas, sitting cross-legged on the bed going through her phone.<</narrative>>
<<dialog "mother">>Oh I was going to come down and say goodnight.<</dialog>>
<<dialog "player">>I beat you to it. Goodnight.<</dialog>>
<<dialog "mother">>(smiling) Goodnight. Sleep well.<</dialog>>`,
friendship: 1
}
],
goodnight: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>She's reading in bed, lamp on, glasses on. She pulls them off when she hears you knock.<</narrative>>
<<dialog "mother">>Hey. Did you need something?<</dialog>>
<<dialog "player">>No, just saying goodnight.<</dialog>>
<<dialog "mother">>(smiling) Goodnight, sweetheart. Sleep well.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>She's just turned off her lamp. You catch her before she falls asleep.<</narrative>>
<<dialog "mother">>(tiredly) Mm?<</dialog>>
<<dialog "player">>Just goodnight.<</dialog>>
<<dialog "mother">>(quietly) Goodnight. Close the door on your way out?<</dialog>>`,
friendship: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-2.webp">>
<<image _img "100%">>
<<narrative>>She's about to turn off her light but pauses when she sees you in the doorway.<</narrative>>
<<dialog "mother">>Come to say goodnight?<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "mother">>Good.
(a pause)
I'm glad we're here. All of us, together. Even if it's an adjustment.<</dialog>>
<<dialog "player">>Me too.<</dialog>>
<<dialog "mother">>Goodnight.<</dialog>>`,
friendship: 2, trust: 1
}
],
reflection: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's at the window, looking out at the quiet street. She turns when she hears you.<</narrative>>
<<dialog "mother">>I was thinking about the old neighborhood.<</dialog>>
<<dialog "player">>Do you miss it?<</dialog>>
<<dialog "mother">>(after a pause) Parts of it. The routine mostly. We'll build a new one here.
(to you)
You'll be okay, you know. We all will.<</dialog>>`,
friendship: 2, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's sorting through a box that still hasn't been unpacked, picking things up and setting them down.<</narrative>>
<<dialog "mother">>Every move, you find things you forgot you kept.<</dialog>>
<<dialog "player">>(looking) What's in there?<</dialog>>
<<dialog "mother">>Old things. Photos, mostly. A few things that don't have a place yet.
(holding something up, considering)
Some things take a while to find their place.<</dialog>>`,
friendship: 1, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level1/parentsroomEvening-3.webp">>
<<image _img "100%">>
<<narrative>>She's sitting quietly on her side of the bed, not doing anything, just being still.<</narrative>>
<<dialog "mother">>You know, mornings are the hardest still. When I wake up and for a second I forget we've moved.<</dialog>>
<<dialog "player">>Does that go away?<</dialog>>
<<dialog "mother">>(nodding) Gradually. One morning you wake up and you just know where you are. And it feels like home.<</dialog>>
<<dialog "player">>How long does that take?<</dialog>>
<<dialog "mother">>(quietly) Different for everyone. But it happens.<</dialog>>`,
friendship: 2, trust: 2
}
]
}
}>>/* ==========================================
MOTHER TALK TOPICS LEVEL 2
friendshipLevel === 2
Structure: setup.motherTopics.level2[locTimeKey][topicName] = [ {text, friendship, trust, love} ]
LEVEL 2 TONE:
→ Warmer, more comfortable, slightly personal
→ Mom shares small things about herself, jokes a little
→ Light physical contact acceptable (touch on shoulder, brief hug)
→ Stat gains slightly higher: friendship +2-3, trust +1-2
→ Conversations feel natural, not just transactional
10 CONTEXTS:
fhKitchen_morning fhKitchen_afternoon fhKitchen_evening
fhLivingroom_morning fhLivingroom_afternoon fhLivingroom_evening
fhBackyard_morning fhBackyard_afternoon
fhParentsRoom_morning fhParentsRoom_evening
========================================== */
<<set setup.motherTopics = setup.motherTopics || {}>>
<<set setup.motherTopics.level2 = {
fhKitchen_morning: {
routine: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's already made coffee for two. She slides one toward you without looking up.<</narrative>>
<<dialog "mother">>Figured you'd be down. You keep the same hours you had when you were little.<</dialog>>
<<dialog "player">>Some things don't change.<</dialog>>
<<dialog "mother">>(smiling into her mug) No. Some things don't.
(quietly)
I like that about you.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's making oatmeal. One bowl. Then she pauses and starts a second one without asking.<</narrative>>
<<dialog "mother">>You want this with raisins or without?<</dialog>>
<<dialog "player">>Without. You remembered I hate raisins?<</dialog>>
<<dialog "mother">>(dryly) You've been hating raisins since you were four. I haven't forgotten.<</dialog>>
<<dialog "player">>(sitting down) Fair enough.<</dialog>>
<<dialog "mother">>There are some things a mother does not forget.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's humming something while she washes fruit at the sink. She doesn't notice you at first.<</narrative>>
<<dialog "player">>What's that song?<</dialog>>
<<dialog "mother">>(startled, then laughing) God, you scared me.
(glancing back)
Just something from the radio. I don't even know the name of it.<</dialog>>
<<dialog "player">>It's nice.<</dialog>>
<<dialog "mother">>(returning to the fruit, quieter) Your grandmother used to hum when she cooked too. I never noticed until I caught myself doing it.<</dialog>>`,
friendship: 2, trust: 2
}
],
memory: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's looking at the fruit bowl. The one she kept from the old house.<</narrative>>
<<dialog "mother">>Do you remember Sunday breakfasts? In the old place?<</dialog>>
<<dialog "player">>Vaguely. Pancakes?<</dialog>>
<<dialog "mother">>Every Sunday. You used to put too much syrup on and then complain your stomach hurt.<</dialog>>
<<dialog "player">>(laughing) I don't remember that part.<</dialog>>
<<dialog "mother">>(warmly) I do.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's standing at the window with her coffee, the morning light on her face.<</narrative>>
<<dialog "mother">>When you were small, you used to wake up before everyone and just sit here. In the kitchen. Just waiting for someone to get up.<</dialog>>
<<dialog "player">>I didn't know you knew that.<</dialog>>
<<dialog "mother">>I'd hear you. I'd watch from the stairs for a bit before coming down.
(sipping)
You were always so patient.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She sets down a plate of toast in front of you. Same way she always has. No fanfare.<</narrative>>
<<dialog "mother">>I used to make toast for you before school. That was the one thing I never had to fight you about in the morning.<</dialog>>
<<dialog "player">>Toast is easy to like.<</dialog>>
<<dialog "mother">>(sitting across from you) Simple things usually are.<</dialog>>`,
friendship: 2, trust: 1
}
],
teasing: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She catches you reaching for her coffee when you think she's not looking.<</narrative>>
<<dialog "mother">>That's mine.<</dialog>>
<<dialog "player">>I was just going to taste it.<</dialog>>
<<dialog "mother">>You were going to drink half of it and pretend you didn't.
(pulling it back, amused)
Go make your own. You know where everything is.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She catches you trying to sneak the last of the biscuits from the tin.<</narrative>>
<<dialog "mother">>I said they had to last the week.<</dialog>>
<<dialog "player">>It is the week. Wednesday is in the week.<</dialog>>
<<dialog "mother">>(folding her arms, fighting a smile) That is technically true.
(pointing)
One. And you didn't see me say that.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She's reading the back of a cereal box. You raise an eyebrow.<</narrative>>
<<dialog "player">>Are you actually reading the cereal box?<</dialog>>
<<dialog "mother">>(not looking up) The nutrition information is interesting.<</dialog>>
<<dialog "player">>It really isn't.<</dialog>>
<<dialog "mother">>(looking up, dignified) I contain multitudes.<</dialog>>`,
friendship: 3, trust: 1
}
]
},
fhKitchen_afternoon: {
cookingTogether: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's prepping for dinner earlier than usual. She looks up when you come in and nods at the counter.<</narrative>>
<<dialog "mother">>I was hoping you'd wander in. Can you chop those onions for me?<</dialog>>
<<dialog "player">>Sure.
(looking at the pile)
That's a lot of onions.<</dialog>>
<<dialog "mother">>Big batch. I'm making enough to freeze some.
(sliding over the board)
You cry, I cook that's the deal.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's teaching you her sauce recipe. Or trying to.<</narrative>>
<<dialog "mother">>A splash. Not a pour. A splash.<</dialog>>
<<dialog "player">>That's a subjective unit of measurement.<</dialog>>
<<dialog "mother">>It is not subjective. A splash is a splash.
(demonstrating)
See? That's a splash.<</dialog>>
<<dialog "player">>It smells amazing whatever it is.<</dialog>>
<<dialog "mother">>(pleased) Of course it does. I've been making this for twenty years.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>You're both in the kitchen at the same time, moving around each other easily.<</narrative>>
<<dialog "mother">>We used to do this more. Just be in the kitchen together.<</dialog>>
<<dialog "player">>We can do it more now.<</dialog>>
<<dialog "mother">>(glancing at you) You mean that?<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "mother">>(back to her work, quietly) Good.<</dialog>>`,
friendship: 3, trust: 2
}
],
advice: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's making tea and sets one in front of you too, unprompted.<</narrative>>
<<dialog "mother">>How are you really doing with all of this? The move, the new start.<</dialog>>
<<dialog "player">>Some days better than others.<</dialog>>
<<dialog "mother">>(sitting) That's honest. I appreciate honest.
(wrapping hands around her mug)
It gets easier. Not all at once but it does.<</dialog>>
<<dialog "player">>Did it for you?<</dialog>>
<<dialog "mother">>Eventually. Give yourself time. You're allowed to not be okay immediately.<</dialog>>`,
friendship: 2, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's drying her hands on a towel, thinking about something.<</narrative>>
<<dialog "mother">>Can I say something without it being a thing?<</dialog>>
<<dialog "player">>...Sure.<</dialog>>
<<dialog "mother">>You've seemed lighter lately. More like yourself.
(meeting your eyes)
I just wanted you to know I noticed. That's all.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>Something on the stove is simmering. She stirs it slowly, talking without turning.<</narrative>>
<<dialog "mother">>You know, when I was your age I thought I had everything figured out. I was very wrong.<</dialog>>
<<dialog "player">>What did you have wrong?<</dialog>>
<<dialog "mother">>Almost everything.
(turning slightly)
But I knew the important things. The people I wanted around me.
(looking at you)
That part I got right.<</dialog>>`,
friendship: 2, trust: 2
}
],
gratitude: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She notices you had cleaned up some dishes earlier. She looks at the counter, then at you.<</narrative>>
<<dialog "mother">>Did you do these?<</dialog>>
<<dialog "player">>Just the ones from this morning.<</dialog>>
<<dialog "mother">>(quiet for a moment) Thank you. That was kind.
(turning back to what she was doing, softer)
It helps. More than you know.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She's putting leftovers in the fridge and pauses, looking at something in her hands.<</narrative>>
<<dialog "mother">>You know I don't always say it. But I'm grateful you're here. That we're all here.<</dialog>>
<<dialog "player">>Me too.<</dialog>>
<<dialog "mother">>(closing the fridge, turning) Okay. Good. That's decided then.
(back to business)
Do we need milk?<</dialog>>
<<dialog "player">>(smiling) Probably.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She hands you a small piece of whatever she's been tasting. No explanation.<</narrative>>
<<dialog "player">>What's this?<</dialog>>
<<dialog "mother">>Just try it.<</dialog>>
<<dialog "player">>(tasting) That's really good.<</dialog>>
<<dialog "mother">>(nods, satisfied) I know. I've been working on that recipe for years.
(a small smile)
I don't let everyone taste it.<</dialog>>`,
friendship: 3
}
]
},
fhKitchen_evening: {
sharingDay: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's finishing dinner prep. The kitchen's warm, the light gentler than the rest of the house.<</narrative>>
<<dialog "mother">>Tell me something good that happened today. Anything.<</dialog>>
<<dialog "player">>Good?<</dialog>>
<<dialog "mother">>One thing. Doesn't have to be big.<</dialog>>
<<dialog "player">>(thinking) I found a coffee place two streets over. Really good coffee.<</dialog>>
<<dialog "mother">>(nodding, genuinely pleased) That's good. That's actually good. A good coffee place matters.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>Dad's set the table for once. Mom notices you noticing.<</narrative>>
<<dialog "mother">>(quietly) I asked him three times over the last two weeks. He did it without being asked tonight.<</dialog>>
<<dialog "player">>Progress.<</dialog>>
<<dialog "mother">>(whispering conspiratorially) Don't make a big deal of it. He'll act like he always does it.<</dialog>>
<<dialog "player">>(matching her tone) Understood.<</dialog>>
<<dialog "mother">>(fighting a smile) Good.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>It's just the two of you. Dad and Jake are elsewhere. She seems lighter for it.<</narrative>>
<<dialog "mother">>I like evenings like this sometimes. Just quiet.<</dialog>>
<<dialog "player">>Just us?<</dialog>>
<<dialog "mother">>I love your father. I love your brother. And sometimes I need five minutes where no one is asking me anything.
(looking at you)
You've always been good at that. Comfortable silence.<</dialog>>`,
friendship: 3, trust: 2
}
],
familyStory: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's stirring something and suddenly laughs. At a memory, you think.<</narrative>>
<<dialog "mother">>I was just thinking about the time you locked yourself in the pantry as a kid. Do you remember that?<</dialog>>
<<dialog "player">>Vaguely. I was looking for something.<</dialog>>
<<dialog "mother">>You were in there for twenty minutes before you knocked. You didn't want to admit you needed help.<</dialog>>
<<dialog "player">>(smiling) That sounds accurate.<</dialog>>
<<dialog "mother">>You were always so stubborn about asking. Still are, I think.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She starts a sentence, stops, then continues like she's decided something.<</narrative>>
<<dialog "mother">>I never told you this, but the night before your first day of school I sat in the kitchen until three in the morning. Couldn't sleep.<</dialog>>
<<dialog "player">>Worried?<</dialog>>
<<dialog "mother">>Terrified. You seemed so small to me. Still do sometimes.
(half-smiling)
Don't tell your brother I said something human.<</dialog>>`,
friendship: 3, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's putting the finishing touches on dinner and hands you a spoon.<</narrative>>
<<dialog "mother">>Your grandfather used to say a meal isn't done until someone else tastes it.<</dialog>>
<<dialog "player">>Smart man.<</dialog>>
<<dialog "mother">>He was. Terrible at directions, brilliant at food. I think about him every time I cook.<</dialog>>
<<dialog "player">>What would he think of this place?<</dialog>>
<<dialog "mother">>(considering) He'd say the kitchen is big enough for two people to work without fighting. High praise from him.<</dialog>>`,
friendship: 2, trust: 2
}
],
compliment: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>You've been helping without her asking for a while now. She sets down her spoon and looks at you.<</narrative>>
<<dialog "mother">>You know, you've grown into someone I genuinely like being around. Not just someone I love someone I like.<</dialog>>
<<dialog "player">>Those are different?<</dialog>>
<<dialog "mother">>(seriously) Very different.
(picking her spoon back up)
It's a compliment. Accept it.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She watches you pour water into two glasses without thinking about it.<</narrative>>
<<dialog "mother">>You always think of other people. I don't think you realise you do it.<</dialog>>
<<dialog "player">>I just filled a glass.<</dialog>>
<<dialog "mother">>You filled two. You didn't think about it.
(quietly)
That's just how you are.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/kitchenEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>End of dinner prep. She touches your arm briefly as she passes.<</narrative>>
<<dialog "mother">>I'm glad you're home.
(continuing to the stove, matter-of-fact)
Now help me carry this in.<</dialog>>
<<dialog "player">>(quietly) Yeah. Me too.<</dialog>>`,
friendship: 3, trust: 2, love: 1
}
]
},
fhLivingroom_morning: {
conversation: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's already made room on the couch. Moved her stuff to one side before you even got there.<</narrative>>
<<dialog "mother">>Come sit. I've been meaning to just talk with you and I keep not doing it.<</dialog>>
<<dialog "player">>(sitting) Talk about anything specific?<</dialog>>
<<dialog "mother">>No. Just talk. How are you actually finding it here? The new area?<</dialog>>
<<dialog "player">>Still figuring it out. But it's starting to feel less strange.<</dialog>>
<<dialog "mother">>(nodding) That's the right direction.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She turns off the TV as you walk in. She'd rather talk.<</narrative>>
<<dialog "mother">>Do you have plans before noon?<</dialog>>
<<dialog "player">>Not really. Why?<</dialog>>
<<dialog "mother">>I was going to take a walk. Around the neighborhood, see what's here. Want to come?<</dialog>>
<<dialog "player">>Sure, actually.<</dialog>>
<<dialog "mother">>(standing, pleased) Good. Give me ten minutes. I look terrible.<</dialog>>
<<dialog "player">>You look fine.<</dialog>>
<<dialog "mother">>I know. I just like the ten minutes.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She asks about something she noticed. A book. A change in your routine.<</narrative>>
<<dialog "mother">>You've been up earlier this week. New habit?<</dialog>>
<<dialog "player">>Trying to be. We'll see if it sticks.<</dialog>>
<<dialog "mother">>(approvingly) Good hours, mornings. Quiet before the world gets loud.
(sipping coffee)
I've been a morning person my whole life. Your father never understood it.<</dialog>>`,
friendship: 2, trust: 1
}
],
oldMemory: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's looking at an old photo she's set on the shelf. It wasn't unpacked until now.<</narrative>>
<<dialog "mother">>Do you remember this?
(showing you the photo)
That summer we went to the coast. You must have been about seven.<</dialog>>
<<dialog "player">>I remember the ice cream. And the sunburn.<</dialog>>
<<dialog "mother">>(laughing) Oh, the sunburn. I put so much cream on you. You smelled like coconut for a week.<</dialog>>
<<dialog "player">>Great holiday though.<</dialog>>
<<dialog "mother">>(looking at the photo) Yes. It was.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's looking out the window at a family walking by. Two parents and a small child.<</narrative>>
<<dialog "mother">>When you were that age, you used to insist on walking on my left. Always the left. No idea why.<</dialog>>
<<dialog "player">>I don't remember that at all.<</dialog>>
<<dialog "mother">>You did it until you were about nine. One day you just stopped and switched sides. Never explained why.
(smiling softly)
You've always done things on your own timeline.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She finds something while sorting a box. A drawing. Badly done, clearly old.<</narrative>>
<<dialog "mother">>(holding it up) Do you know what this is supposed to be?<</dialog>>
<<dialog "player">>(looking) A dog?<</dialog>>
<<dialog "mother">>You told me it was our family. You were six. I said nothing because I didn't want to upset you.<</dialog>>
<<dialog "player">>That's the kindest lie you've ever told.<</dialog>>
<<dialog "mother">>(putting it back carefully) I thought so too.<</dialog>>`,
friendship: 3, trust: 2
}
],
comfort: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She can tell something's on your mind. She doesn't push. She just makes space.<</narrative>>
<<dialog "mother">>You look like you're thinking hard about something.<</dialog>>
<<dialog "player">>Just a lot in my head lately.<</dialog>>
<<dialog "mother">>Do you want to talk about it?<</dialog>>
<<dialog "player">>Not really. Not yet.<</dialog>>
<<dialog "mother">>(simply) Okay.
(shifting to give you more room)
Then just sit here for a bit. You don't have to say anything.<</dialog>>`,
friendship: 2, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She reaches over and squeezes your hand. Just once. Then lets go.<</narrative>>
<<dialog "mother">>I don't say this enough. But you're doing well. Whatever it feels like from the inside from the outside, you're doing really well.<</dialog>>
<<dialog "player">>It doesn't always feel that way.<</dialog>>
<<dialog "mother">>I know. But it's true anyway.<</dialog>>`,
friendship: 3, trust: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She notices you've been quieter lately and brings you tea without making it a whole thing.<</narrative>>
<<dialog "mother">>Here.
(setting it down)
I put honey in it. I know you don't usually ask for it. Today seemed like a honey day.<</dialog>>
<<dialog "player">>(looking at the mug) How do you do that?<</dialog>>
<<dialog "mother">>(sitting) Twenty years of practice.<</dialog>>`,
friendship: 3, trust: 2
}
]
},
fhLivingroom_afternoon: {
deeperChat: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's settled on the couch like she has nowhere to be. That's rare.<</narrative>>
<<dialog "mother">>Can I ask you something?<</dialog>>
<<dialog "player">>Sure.<</dialog>>
<<dialog "mother">>Is there anything about this move you're actually glad about? Even one small thing?<</dialog>>
<<dialog "player">>(thinking) The light in my room is better. The window faces east.<</dialog>>
<<dialog "mother">>(nodding) Good light makes a difference. More than people think.
(pause)
That's a good answer.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She brings up something she'd been holding back. Gently.<</narrative>>
<<dialog "mother">>I know the move was hard on you. More than you said.<</dialog>>
<<dialog "player">>It was an adjustment.<</dialog>>
<<dialog "mother">>(evenly) You don't have to protect me from that. You're allowed to have found it hard.<</dialog>>
<<dialog "player">>(after a pause) It was hard.<</dialog>>
<<dialog "mother">>(nodding) Thank you. For telling me.
(quietly)
We'll make it worth it. I promise.<</dialog>>`,
friendship: 3, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's venting a little. Then stops herself and laughs.<</narrative>>
<<dialog "mother">>Sorry. I'm not I don't usually do this.<</dialog>>
<<dialog "player">>You can. It's fine.<</dialog>>
<<dialog "mother">>(looking at you) You're not going to tell your father?<</dialog>>
<<dialog "player">>Tell him what? You said nothing.<</dialog>>
<<dialog "mother">>(settling back, relieved) Exactly.
(a pause)
You're actually quite good at this.<</dialog>>`,
friendship: 3, trust: 3
}
],
humor: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She tells you something she saw that day. Something absurd. She's still delighted about it.<</narrative>>
<<dialog "mother">>The man at the market was arguing with a self-checkout machine for ten minutes. A full argument. Gesturing and everything.<</dialog>>
<<dialog "player">>Did the machine win?<</dialog>>
<<dialog "mother">>(laughing) It didn't even blink. Very stoic. I respected it.<</dialog>>
<<dialog "player">>Sounds like a fair fight.<</dialog>>
<<dialog "mother">>He eventually walked away. The machine clearly won on points.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She gets you with a joke she's clearly been sitting on.<</narrative>>
<<dialog "mother">>Ask me how I am.<</dialog>>
<<dialog "player">>(suspicious) ...How are you?<</dialog>>
<<dialog "mother">>Wonderful, thanks for asking. You never ask first, you know.<</dialog>>
<<dialog "player">>That was a setup.<</dialog>>
<<dialog "mother">>(shrugging, pleased) Completely. But you fell for it.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She catches you mid-yawn, points silently, and writes something in the air.<</narrative>>
<<dialog "player">>What are you writing?<</dialog>>
<<dialog "mother">>Evidence. For when you tell me you don't need more sleep.<</dialog>>
<<dialog "player">>I slept fine.<</dialog>>
<<dialog "mother">>(serenely) That yawn tells a different story.<</dialog>>`,
friendship: 3
}
],
confiding: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She tells you something small. A detail about herself she doesn't usually share.<</narrative>>
<<dialog "mother">>You know I used to want to be a landscape architect. Before everything else.<</dialog>>
<<dialog "player">>Really?<</dialog>>
<<dialog "mother">>I was good at it. At least I thought so.
(looking at the window)
Life changes your plans. You adapt.<</dialog>>
<<dialog "player">>Do you regret it?<</dialog>>
<<dialog "mother">>(honestly) Some days. Most days, no. I just like knowing I had another life in mind once.<</dialog>>`,
friendship: 3, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She admits something with a small, rueful laugh.<</narrative>>
<<dialog "mother">>I haven't been sleeping particularly well since we moved.<</dialog>>
<<dialog "player">>I didn't know that.<</dialog>>
<<dialog "mother">>I didn't want to make it everyone else's issue.
(glancing at you)
But you asked how I was or you would have, eventually so. There it is.<</dialog>>
<<dialog "player">>Is there anything that helps?<</dialog>>
<<dialog "mother">>(thinking) You being home, actually. It's a bit silly.<</dialog>>`,
friendship: 3, trust: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She says something she's clearly been turning over for a while.<</narrative>>
<<dialog "mother">>I know I don't always make it easy to come to me with things. I know I can be a bit contained.<</dialog>>
<<dialog "player">>You're not that contained.<</dialog>>
<<dialog "mother">>(raising an eyebrow) Your father would disagree. And he's not wrong.
(pause)
I'm trying to be better at it. The door being open kind of thing.<</dialog>>`,
friendship: 3, trust: 3
}
]
},
fhLivingroom_evening: {
bonding: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's got a film on. Something she picked for both of you, clearly.<</narrative>>
<<dialog "mother">>I thought you might want to watch something. Nothing requires thinking. Just sit.<</dialog>>
<<dialog "player">>(sitting) What is it?<</dialog>>
<<dialog "mother">>Something I used to love. I want to see if it holds up.
(glancing at you)
You're the test.<</dialog>>
<<dialog "player">>No pressure.<</dialog>>
<<dialog "mother">>(focusing back on the screen) None at all.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She makes space on the couch, puts a cushion there. Like she hoped you'd come.<</narrative>>
<<dialog "mother">>I made enough tea for two. In case you wanted to sit here a while.<</dialog>>
<<dialog "player">>(sitting) In case?<</dialog>>
<<dialog "mother">>(lightly) You're very predictable. I mean that kindly.<</dialog>>
<<dialog "player">>Comfortingly predictable?<</dialog>>
<<dialog "mother">>(reaching for her tea) Exactly.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>You're both watching something and she laughs. Really laughs. At the same moment you do.<</narrative>>
<<dialog "mother">>(still laughing) See? I knew you'd get that.<</dialog>>
<<dialog "player">>You knew?<</dialog>>
<<dialog "mother">>I know your sense of humour. I've been studying it for twenty years.
(settling back)
I'm very qualified.<</dialog>>`,
friendship: 3
}
],
openingUp: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>The room's quieter than usual. She says something she's been holding.<</narrative>>
<<dialog "mother">>Can I tell you something I haven't told your father?<</dialog>>
<<dialog "player">>Sure.<</dialog>>
<<dialog "mother">>I'm actually happier here than I let on. I didn't want to make the move feel easy when it was hard for everyone else.
(pause)
But quietly yes. I think this is going to be good.<</dialog>>`,
friendship: 3, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She confides something mid-commercial break. The kind of thing people say when they aren't quite looking at you.<</narrative>>
<<dialog "mother">>You know, this is the first home where I feel like I chose it. The others just... happened.<</dialog>>
<<dialog "player">>This one feels different?<</dialog>>
<<dialog "mother">>A bit. I just wanted that on record somewhere.
(TV comes back on)
Right. Where were we.<</dialog>>`,
friendship: 3, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She says it quietly, not quite looking at you, but meaning it completely.<</narrative>>
<<dialog "mother">>I know I wasn't always easy to talk to. When you were younger.<</dialog>>
<<dialog "player">>You were fine.<</dialog>>
<<dialog "mother">>I was busy. There's a difference. I could have been better at the small things.
(finally looking at you)
I'm trying to be better now. If you'll let me.<</dialog>>`,
friendship: 3, trust: 3, love: 1
}
],
warmGoodnight: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She stands and stretches, then squeezes your shoulder as she passes.<</narrative>>
<<dialog "mother">>Going up. Goodnight.
(pausing at the door)
It's nice, having you here. Even just sitting.<</dialog>>
<<dialog "player">>Night, Mom.<</dialog>>`,
friendship: 3, trust: 1, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She collects the mugs and pauses at the door on her way out.<</narrative>>
<<dialog "mother">>Don't stay up too much longer. You need sleep even when you don't think you do.<</dialog>>
<<dialog "player">>I know.<</dialog>>
<<dialog "mother">>(dryly) And yet.
(softer)
Goodnight. I'm glad you were here tonight.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/livingroomEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She pulls you into a brief hug before heading upstairs. No speech, no ceremony.<</narrative>>
<<dialog "mother">>Goodnight, sweetheart.<</dialog>>
<<dialog "player">>(hugging back) Night.<</dialog>>
<<narrative>>She lets go and heads up the stairs. The house feels warmer somehow.<</narrative>>`,
friendship: 3, trust: 1, love: 2
}
]
},
fhBackyard_morning: {
ambitions: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's drawn something on a napkin. A rough plan for the garden.<</narrative>>
<<dialog "mother">>Look. By summer, I want this area to be herbs. And this corner flowers for cutting.<</dialog>>
<<dialog "player">>(looking) You have a whole plan.<</dialog>>
<<dialog "mother">>I always have a plan. The execution is the challenge.
(looking at the space)
But I think this might actually work.<</dialog>>
<<dialog "player">>I think so too.<</dialog>>
<<dialog "mother">>(quietly satisfied) Good.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's walking the perimeter of the garden with the energy of someone laying claim to it.<</narrative>>
<<dialog "mother">>I want a bench here. And a proper table there for when we eat outside.
(turning)
Do you think we would? Eat outside?<</dialog>>
<<dialog "player">>In summer, sure.<</dialog>>
<<dialog "mother">>(nodding, energised) Yes. Sunday lunches. Outside. We haven't done that in years.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She holds out a seed packet toward you. An invitation, basically.<</narrative>>
<<dialog "mother">>I bought tomato seeds. I've never successfully grown tomatoes.<</dialog>>
<<dialog "player">>This time?<</dialog>>
<<dialog "mother">>(looking at the packet) I've been told I overwater them. I feel I am going to overwater them again.
(handing them to you)
Maybe you can stop me if I try.<</dialog>>`,
friendship: 3
}
],
childhood: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's looking at the garden and you can tell she's somewhere else for a moment.<</narrative>>
<<dialog "mother">>My mother had a garden. Much bigger than this. We used to follow her around in it when we were small.<</dialog>>
<<dialog "player">>Did you like it? Gardening?<</dialog>>
<<dialog "mother">>As a child I mostly liked eating what came out of it.
(kneeling by a bed)
But something stuck. Obviously.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She finds an old trowel in the shed. It's clearly been used. She holds it up.<</narrative>>
<<dialog "mother">>I've had this since my first flat. I took it from my parents without asking.<</dialog>>
<<dialog "player">>Did they notice?<</dialog>>
<<dialog "mother">>(grinning) If they did, they never said. Probably figured I needed it more than they did.
(getting to work)
They were right.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She points to a corner of the yard, explaining something to herself as much as to you.<</narrative>>
<<dialog "mother">>We had a rosebush when I was seven. Same spot in every house my mother moved it every time. She said it was the same bush but I never quite believed her.<</dialog>>
<<dialog "player">>Maybe it was.<</dialog>>
<<dialog "mother">>(considering) Maybe. I'd like to think it was.
(quietly)
I should plant one here.<</dialog>>`,
friendship: 3, trust: 2
}
],
sharedSilence: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>You're both in the garden doing different things. She's planting, you're just there. Neither of you needs to fill the quiet.<</narrative>>
<<dialog "mother">>(after a long quiet) This is nice.<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<dialog "mother">>Good.
(back to her work)
Stay as long as you like.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She pats the step beside her. No words needed.<</narrative>>
<<dialog "player">>(sitting) Cold morning.<</dialog>>
<<dialog "mother">>Mm. Coffee helps.
(wrapping both hands around her mug)
We don't have to talk if you don't want to.<</dialog>>
<<dialog "player">>I know.<</dialog>>
<<dialog "mother">>(nodding) Good. Me neither.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>You sit beside her in the garden. She leans her shoulder against yours briefly. Then pulls back to whatever she was doing.<</narrative>>
<<dialog "mother">>(softly) Good morning.<</dialog>>
<<dialog "player">>Morning.<</dialog>>
<<narrative>>The yard is quiet. She goes back to her coffee. So do you.<</narrative>>`,
friendship: 3, trust: 2, love: 1
}
]
},
fhBackyard_afternoon: {
lifeAdvice: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's pruning something, focused, choosing carefully what to cut.<</narrative>>
<<dialog "mother">>Can I give you some unsolicited advice?<</dialog>>
<<dialog "player">>Go ahead.<</dialog>>
<<dialog "mother">>Don't try to fix everything at once. Pick one thing, do that well, then move to the next.
(clipping carefully)
The rest has a way of sorting itself when you're not staring at all of it.<</dialog>>`,
friendship: 2, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She sets down her tools and sits beside you, getting philosophical.<</narrative>>
<<dialog "mother">>Can I tell you the one thing I wish someone had told me at your age?<</dialog>>
<<dialog "player">>Please.<</dialog>>
<<dialog "mother">>Most of the things you're worrying about right now won't matter in five years. And the ones that do you'll handle them.
(pause)
That's all. Go on.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's been quiet for a while, then turns like she's decided something.<</narrative>>
<<dialog "mother">>You're braver than you think. I've seen it. The way you handle things.<</dialog>>
<<dialog "player">>I don't always feel that way.<</dialog>>
<<dialog "mother">>Bravery's never felt brave from the inside. That's kind of the point of it.<</dialog>>`,
friendship: 3, trust: 2
}
],
laughter: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's trying to identify a plant and absolutely failing.<</narrative>>
<<dialog "mother">>Is this a weed or something I planted?<</dialog>>
<<dialog "player">>(looking) I have no idea.<</dialog>>
<<dialog "mother">>Excellent. Between the two of us we know nothing.
(pulling it out anyway)
It's coming out. The uncertainty was stressing me out.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She tries to carry three things at once and they all nearly topple. You catch one.<</narrative>>
<<dialog "player">>Here.<</dialog>>
<<dialog "mother">>(surrendering it) I had it.<</dialog>>
<<dialog "player">>You very much did not have it.<</dialog>>
<<dialog "mother">>(trying not to laugh) I had it conceptually.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She reads something off her phone and looks genuinely confused.<</narrative>>
<<dialog "mother">>The gardening app says I should be talking to my plants.<</dialog>>
<<dialog "player">>Does it say what to say?<</dialog>>
<<dialog "mother">>It says encouraging things.
(staring at a plant)
Encouragingly.
(to the plant, deadpan)
Well done.<</dialog>>
<<dialog "player">>(laughing) Perfect.<</dialog>>
<<dialog "mother">>(satisfied) I think that went well.<</dialog>>`,
friendship: 3
}
],
memory: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She's sitting with a cup of tea, remembering something out loud.<</narrative>>
<<dialog "mother">>We had a garden once, when you were very small. Before we moved the first time. You used to sit in the middle of it and just look at everything.
(smiling)
Not touching. Just watching. Like you were studying it.<</dialog>>
<<dialog "player">>I don't remember that.<</dialog>>
<<dialog "mother">>No. But I do. I took a picture once I should find it.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She points at the back fence with her mug.<</narrative>>
<<dialog "mother">>See that corner? The light hits it the same as our old garden. I noticed it the day we viewed the house.<</dialog>>
<<dialog "player">>Is that why you picked it?<</dialog>>
<<dialog "mother">>(honestly) Partly. Your father thought I was being sentimental.
(a pause)
He was right. But it matters, the light.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/backyardAfternoon-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She's quiet, looking at the sky the way people do when a memory arrives unexpectedly.<</narrative>>
<<dialog "mother">>Your grandmother would have loved this yard. She always said you could tell a family by their garden. Whether it was cared for.
(looking around)
I think she'd approve of what we're doing here.<</dialog>>
<<dialog "player">>I think so too.<</dialog>>
<<dialog "mother">>(softly) Good.<</dialog>>`,
friendship: 3, trust: 2
}
]
},
fhParentsRoom_morning: {
compliment: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She looks at you when you come in. Really looks. Tilts her head slightly.<</narrative>>
<<dialog "mother">>You look well this morning.<</dialog>>
<<dialog "player">>Do I?<</dialog>>
<<dialog "mother">>Rested. Settled.
(back to her mirror)
It suits you. Being here suits you.<</dialog>>`,
friendship: 3, trust: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She catches you in the hallway and just says it, like she'd been holding it.<</narrative>>
<<dialog "mother">>You've been great with Jake lately. Whatever you're doing, keep doing it.<</dialog>>
<<dialog "player">>I'm just being around.<</dialog>>
<<dialog "mother">>That's exactly what he needs. And you know that. That's the part I'm proud of.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She hands you something she picked up. Small, nothing expensive. She just thought of you.<</narrative>>
<<dialog "mother">>I saw this and thought of you.
(shrugging before you can react)
It's nothing. Just a small thing.<</dialog>>
<<dialog "player">>(looking at it) Thank you. That's really thoughtful.<</dialog>>
<<dialog "mother">>(turning back to her mirror) It's nothing. I like that I thought of you.<</dialog>>`,
friendship: 3, trust: 1, love: 1
}
],
familyTalk: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She lowers her voice slightly. Family-meeting tone, without the meeting.<</narrative>>
<<dialog "mother">>Can I ask how you think your father's doing? With the new job.<</dialog>>
<<dialog "player">>He seems okay. Tired, but okay.<</dialog>>
<<dialog "mother">>(nodding) He doesn't say much. I'm watching, but I don't want to push.
(pause)
Just tell me if you notice anything.<</dialog>>
<<dialog "player">>I will.<</dialog>>`,
friendship: 3, trust: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She's thinking out loud before you've properly woken up. Gently, though.<</narrative>>
<<dialog "mother">>I think we should do something together. As a family. All four of us. Before everyone gets too busy.<</dialog>>
<<dialog "player">>What did you have in mind?<</dialog>>
<<dialog "mother">>Nothing huge. Dinner out. A day somewhere. Just something.
(looking at you)
Would you be up for it?<</dialog>>
<<dialog "player">>Yeah. Definitely.<</dialog>>
<<dialog "mother">>(satisfied) Good. I'll think of something good.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She mentions Jake. She has the voice she uses when she's been holding something since last night.<</narrative>>
<<dialog "mother">>Jake seems better this week. I think the school is starting to feel more normal for him.<</dialog>>
<<dialog "player">>He mentioned a friend actually. Yesterday.<</dialog>>
<<dialog "mother">>(relieved) Did he? Oh, good. That's good.
(exhaling)
That's very good.<</dialog>>`,
friendship: 3, trust: 2
}
],
warmth: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She's doing up a button she keeps missing. You wait. She catches your eye in the mirror and laughs.<</narrative>>
<<dialog "mother">>Don't say anything.<</dialog>>
<<dialog "player">>I wasn't going to.<</dialog>>
<<dialog "mother">>You were thinking it.<</dialog>>
<<dialog "player">>Good morning to you too.<</dialog>>
<<dialog "mother">>(managing the button finally, triumphant) Good morning.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She turns from the mirror and looks at you with something unguarded in her expression.<</narrative>>
<<dialog "mother">>I'm glad you're here. I know I say it but I mean it properly.<</dialog>>
<<dialog "player">>I know you do.<</dialog>>
<<dialog "mother">>(turning back) Good. Right.
(more briskly)
Is there coffee still?<</dialog>>
<<dialog "player">>Should be.<</dialog>>
<<dialog "mother">>Then I'll see you downstairs.<</dialog>>`,
friendship: 3, trust: 1, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomMorning-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She gives you a brief hug on her way out. Longer than usual.<</narrative>>
<<dialog "mother">>Good morning.
(releasing you, heading out)
Come down when you're ready. There's plenty.<</dialog>>
<<dialog "player">>(quietly) Morning.<</dialog>>`,
friendship: 3, trust: 1, love: 1
}
]
},
fhParentsRoom_evening: {
appreciation: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's winding down, sitting on the bed. She looks up when you knock and really smiles. Tired, but a real one.<</narrative>>
<<dialog "mother">>Come in. Good day?<</dialog>>
<<dialog "player">>Yeah, actually. You?<</dialog>>
<<dialog "mother">>Better than most. I got things done and no one needed anything catastrophic from me.
(lightly)
I consider that a full success.<</dialog>>`,
friendship: 3
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She says it before you even sit down. Like she'd been saving it.<</narrative>>
<<dialog "mother">>Thank you for today. For being around, for helping. It all adds up.
(pause)
I don't always say it enough.<</dialog>>
<<dialog "player">>You don't have to always say it.<</dialog>>
<<dialog "mother">>(firmly) I do. I want to.
(softer)
So. Thank you.<</dialog>>`,
friendship: 3, trust: 2, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 1 + ".webp">>
<<image _img "100%">>
<<narrative>>She's reflecting on the day out loud, because you're there and she trusts you with it.<</narrative>>
<<dialog "mother">>Some days I feel like I'm managing everything and still getting none of it right.<</dialog>>
<<dialog "player">>That's not true. We notice.<</dialog>>
<<dialog "mother">>(looking at you) Do you?<</dialog>>
<<dialog "player">>Yeah. We do.<</dialog>>
<<dialog "mother">>(quietly) Okay. I'll believe you.<</dialog>>`,
friendship: 3, trust: 3, love: 1
}
],
heartfelt: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She pulls you into a proper hug this time. Longer than usual. Like she needed it.<</narrative>>
<<dialog "mother">>(quietly) Goodnight.
(pulling back)
I love you. You know that, even when I don't say it enough?<</dialog>>
<<dialog "player">>I know.<</dialog>>
<<dialog "mother">>Good. Goodnight.<</dialog>>`,
friendship: 3, trust: 2, love: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She says something that feels like it's been waiting a long time to come out.<</narrative>>
<<dialog "mother">>When you were a teenager, you used to lock yourself away and I never knew what to do with that. I still think about it.
(looking at you)
I'm glad we're different now. That it got easier between us.<</dialog>>
<<dialog "player">>Me too.<</dialog>>
<<dialog "mother">>(simply) Good. Goodnight.<</dialog>>`,
friendship: 3, trust: 3, love: 1
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 2 + ".webp">>
<<image _img "100%">>
<<narrative>>She cups your face for just a moment before letting go.<</narrative>>
<<dialog "mother">>You're going to be alright. All of this you're going to be alright.<</dialog>>
<<dialog "player">>(quietly) Yeah.<</dialog>>
<<dialog "mother">>I know. Goodnight.<</dialog>>`,
friendship: 3, trust: 2, love: 2
}
],
future: [
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She's looking out the window at the night street, thoughtful.<</narrative>>
<<dialog "mother">>Do you know what you want? Beyond this, I mean. For yourself.<</dialog>>
<<dialog "player">>I have some ideas. Not a plan yet.<</dialog>>
<<dialog "mother">>That's fine. Ideas first, then plans. The people who get it backwards usually end up in jobs they hate.
(turning)
What are the ideas?<</dialog>>
<<dialog "player">>(sitting) I'll tell you sometime. When they're clearer.<</dialog>>
<<dialog "mother">>I'll hold you to that.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She shares something she doesn't usually put into words. A hope for the family.<</narrative>>
<<dialog "mother">>I have this image in my head. A few years from now. All of us around a table, laughing about something. Your father burning something on the grill.<</dialog>>
<<dialog "player">>(smiling) He does always burn something.<</dialog>>
<<dialog "mother">>He does. And somehow it always tastes fine anyway.
(looking at you)
I just want that. Simply that.<</dialog>>`,
friendship: 3, trust: 2
},
{
text: `<<set _img = "assets/content/scenes/interactions/mother/level2/parentsroomEvening-" + 3 + ".webp">>
<<image _img "100%">>
<<narrative>>She says goodnight but pauses at the door, one hand on the frame.<</narrative>>
<<dialog "mother">>Whatever happens next with things, with life I want you to know this house is yours too. Properly yours.
(simply)
Goodnight.<</dialog>>
<<dialog "player">>(quietly) Goodnight.<</dialog>>`,
friendship: 3, trust: 3, love: 1
}
]
}
}>>/* ==========================================
BIOLOGICAL NEEDS SYSTEM
Hunger, Thirst, Bladder, Hygiene, Calories
========================================== */
/* EAT FOOD */
/* Usage: <<eatFood 500 30>> (500 calories, -30 hunger) */
<<widget "eatFood">>
<<set _calories = $args[0] || 300>>
<<set _hungerReduction = $args[1] || 25>>
/* Add to daily calorie intake */
<<if $gameSettings.trackCalories>>
<<set $dailyCalorieIntake += _calories>>
<</if>>
/* Reduce hunger (if tracking enabled) */
<<if $gameSettings.trackHunger>>
<<set _hungerNow = Number($hunger)>>
<<if isNaN(_hungerNow)>><<set _hungerNow = 0>><</if>>
<<set _negHunger = -1 * _hungerReduction>>
<<set $hunger = Math.max(0, _hungerNow - _hungerReduction)>>
<<queueStatChange "hunger" _negHunger>>
<</if>>
<</widget>>
/* BURN CALORIES */
/* Usage: <<burnCalories 300 10>> (300 calories burned, +10 hunger) */
<<widget "burnCalories">>
<<set _calories = $args[0] || 200>>
<<set _hungerIncrease = $args[1] || 8>>
/* Subtract from daily calorie intake */
<<if $gameSettings.trackCalories>>
<<set $dailyCalorieIntake -= _calories>>
<</if>>
/* Increase hunger (if tracking enabled) */
<<if $gameSettings.trackHunger>>
<<set $hunger = Math.min(100, $hunger + _hungerIncrease)>>
<</if>>
<</widget>>
/* INCREASE HUNGER (Time-based) */
/* Usage: <<increaseHunger 5>> */
<<widget "increaseHunger">>
<<if typeof $hunger !== "number">><<set $hunger = 0>><</if>>
<<if !$gameSettings.trackHunger>>
<<return>>
<</if>>
<<set _amount = $args[0] || 5>>
<<set $hunger = Math.min(100, $hunger + _amount)>>
<<queueStatChange "hunger" _amount>>
/* Check for critical hunger levels and apply penalties (skip during sleep/relax time) */
<<if !$isSleeping && $timeAdvanceMode !== "relax">>
<<if $hunger >= 90>>
<<set $energy = Math.max(0, $energy - 20)>>
<<queueStatChange "energy" -20>>
<<set $mood = Math.max(0, $mood - 15)>>
<<queueStatChange "mood" -15>>
<<set $health = Math.max(0, $health - 10)>>
<<queueStatChange "health" -10>>
<<elseif $hunger >= 60>>
<<set $energy = Math.max(0, $energy - 10)>>
<<queueStatChange "energy" -10>>
<<set $mood = Math.max(0, $mood - 5)>>
<<queueStatChange "mood" -5>>
<</if>>
<</if>>
<</widget>>
/* INCREASE THIRST (Time-based) */
/* Usage: <<increaseThirst 5>> */
<<widget "increaseThirst">>
<<if typeof $thirst !== "number">><<set $thirst = 0>><</if>>
<<if !$gameSettings.trackThirst>>
<<return>>
<</if>>
<<set _amount = $args[0] || 5>>
<<set $thirst = Math.min(100, $thirst + _amount)>>
<<queueStatChange "thirst" _amount>>
/* Penalties for Dehydration (skip during sleep/relax time) */
<<if !$isSleeping && $timeAdvanceMode !== "relax">>
<<if $thirst >= 90>>
<<set $energy = Math.max(0, $energy - 15)>>
<<queueStatChange "energy" -15>>
<<set $focus = Math.max(0, $focus - 20)>>
<<queueStatChange "focus" -20>>
<<set $health = Math.max(0, $health - 5)>>
<<queueStatChange "health" -5>>
<<elseif $thirst >= 60>>
<<set $energy = Math.max(0, $energy - 5)>>
<<queueStatChange "energy" -5>>
<<set $focus = Math.max(0, $focus - 10)>>
<<queueStatChange "focus" -10>>
<</if>>
<</if>>
<</widget>>
/* INCREASE BLADDER (Time-based) */
/* Usage: <<increaseBladder 5>> */
<<widget "increaseBladder">>
<<if typeof $bladder !== "number">><<set $bladder = 0>><</if>>
<<if !$gameSettings.trackBladder>>
<<return>>
<</if>>
<<set _amount = $args[0] || 5>>
<<set $bladder = Math.min(100, $bladder + _amount)>>
<<queueStatChange "bladder" _amount>>
/* Penalties for Holding it (skip during sleep/relax time) */
<<if !$isSleeping && $timeAdvanceMode !== "relax">>
<<if $bladder >= 100>>
<<set $stress = Math.min(100, $stress + 10)>>
<<queueStatChange "stress" 10>>
<<set $mood = Math.max(0, $mood - 10)>>
<<queueStatChange "mood" -10>>
<<elseif $bladder >= 80>>
<<set $stress = Math.min(100, $stress + 5)>>
<<queueStatChange "stress" 5>>
<</if>>
<</if>>
<</widget>>
/* DECREASE HYGIENE (Time-based) */
/* Usage: <<decreaseHygiene 5>> */
<<widget "decreaseHygiene">>
<<if typeof $hygiene !== "number">><<set $hygiene = 100>><</if>>
<<if !$gameSettings.hygieneRequirement>>
<<return>>
<</if>>
<<set _amount = $args[0] || 5>>
<<set _negAmount = -1 * _amount>>
<<set $hygiene = Math.max(0, $hygiene - _amount)>>
<<queueStatChange "hygiene" _negAmount>>
/* Penalties for Poor Hygiene (skip during sleep/relax time) */
<<if !$isSleeping && $timeAdvanceMode !== "relax">>
<<if $hygiene <= 10>>
<<set $mood = Math.max(0, $mood - 10)>>
<<queueStatChange "mood" -10>>
<</if>>
<</if>>
<</widget>>
/* DECREASE AROUSAL (Time-based) */
/* Usage: <<decreaseArousal 5>> */
<<widget "decreaseArousal">>
<<if typeof $arousal !== "number">><<set $arousal = 0>><</if>>
<<set _amount = Number($args[0])>>
<<if isNaN(_amount) || _amount <= 0>><<set _amount = 10>><</if>>
<<set _before = $arousal>>
<<set $arousal = Math.max(0, $arousal - _amount)>>
<<set _actualDelta = $arousal - _before>>
<<if _actualDelta !== 0>>
<<queueStatChange "arousal" _actualDelta>>
<</if>>
<</widget>>
<<nobr>>
<<set _swapTargetCharId = $interactingChar>>
<<if (!_swapTargetCharId || !$characters[_swapTargetCharId]) && $phoneSwapTargetCharId && $characters[$phoneSwapTargetCharId]>>
<<set _swapTargetCharId = $phoneSwapTargetCharId>>
<</if>>
<<unset $phoneSwapTargetCharId>>
<<if !_swapTargetCharId || !$characters[_swapTargetCharId]>>
<<goto $location>>
<</if>>
<<set _char = setup.getCharacter(_swapTargetCharId)>>
<<set _charName = _char ? ((_char.firstName || _char.name || "") + (_char.lastName ? " " + _char.lastName : "")) : _swapTargetCharId>>
<<set _charState = $characters[_swapTargetCharId]>>
<<if !_charState.currentLocation>><<updateCharacterLocations>><<set _charState = $characters[_swapTargetCharId]>><</if>>
<<set _charLoc = _charState.currentLocation || "unknown">>
<<set $location = _charLoc>>
<<set _charLocName = (typeof window.getLocationName === 'function') ? window.getLocationName(_charLoc) : (setup.navCards[_charLoc] && setup.navCards[_charLoc].name) || _charLoc>>
<<silently>>
<<phoneUnlockContact _swapTargetCharId>>
<<advanceTime 5>>
<<if window.persistPhoneChanges>><<run window.persistPhoneChanges()>><</if>>
<<if window.updatePhoneBadges>><<run window.updatePhoneBadges()>><</if>>
<<run window.notifySuccess(_charName + "'s number added to your contacts!")>>
<</silently>>
<<set _swapReplies = [
"Sure, why not! Here, add me.",
"Of course! Let me get my phone.",
"Yeah, that'd be nice. One sec.",
"Sure thing!",
"Absolutely! Here you go.",
"Why not? We can stay in touch.",
"Sure! I'd like that.",
"Yeah, good idea. Let me pull it up.",
"Of course! One moment.",
"Sure! It's always nice to have another contact."
]>>
<<set _swapReply = _swapReplies.random()>>
<<narrative _charLocName>>
You pull out your phone.
<</narrative>>
<<dialog "player">>
Would you swap numbers with me?
<</dialog>>
<<dialog _swapTargetCharId>>
<<print _swapReply>>
<</dialog>>
<<set _swapVids = [
"assets/content/phone/scenes/swap/numberSwap1.mp4",
"assets/content/phone/scenes/swap/numberSwap2.mp4",
"assets/content/phone/scenes/swap/numberSwap3.mp4"
]>>
<<set _swapVid = _swapVids.random()>>
<<vid _swapVid "100%">>
<<narrative>>
You exchange numbers, saving each other's contact. Now you can text or call whenever you like.
<</narrative>>
<div class="location-actions">
<<btn "Continue" "CharacterInteraction">><</btn>>
</div>
<</nobr>>/* ==========================================
PHONE WIDGETS – Messages, Calls, Gallery
Data model: phone_system_data_and_technical.md
All state updates go through these widgets.
========================================== */
/* Add a photo/video to the gallery from a passage. Usage:
<<GalleryAdd "assets/content/phone/special/quest_photo.webp">>
<<GalleryAdd "path" "photos" "received" 75 "mother">> (path, kind, category, quality, from)
Defaults: kind=photos, category=received, quality=50, from=player
*/
<<widget "GalleryAdd">><<nobr>>
<<if !$args[0]>><<return>><</if>>
<<run (function(){
if (typeof window.phoneGalleryAddItem !== "function") return;
var a = $args;
var path = a[0];
var opts = { kind: a[1] || "photos", category: a[2] || "received", from: a[4] || "player" };
if (a[3] != null && a[3] !== "") opts.quality = parseInt(a[3], 10) || 50;
window.phoneGalleryAddItem(path, opts);
})()>>
<</nobr>><</widget>>
<<widget "phoneReceiveMessage">><<nobr>>
<<set _charId = $args[0]>>
<<set _text = $args[1] || "">>
<<if !_charId || !_text>><<return>><</if>>
<<if !$phoneConversations>><<set $phoneConversations = {}>><</if>>
<<if !$phoneConversations[_charId]>><<set $phoneConversations[_charId] = []>><</if>>
<<set _t = $timeSys || {}>>
<<set _time = { day: _t.day, month: _t.month, year: _t.year, hour: _t.hour, minute: _t.minute }>>
<<run $phoneConversations[_charId].push({ from: _charId, text: _text, time: _time, read: false })>>
<<set _limit = 100>>
<<if $phoneConversations[_charId].length > _limit>>
<<run $phoneConversations[_charId].splice(0, $phoneConversations[_charId].length - _limit)>>
<</if>>
<<if window.notifyPhone>><<run window.notifyPhone("New message")>><</if>>
<<if window.updatePhoneBadges>><<run window.updatePhoneBadges()>><</if>>
<</nobr>><</widget>>
<<widget "phoneSendMessage">><<nobr>>
<<set _charId = $args[0]>>
<<set _text = $args[1] || "">>
<<if !_charId>><<return>><</if>>
<<if !$phoneConversations>><<set $phoneConversations = {}>><</if>>
<<if !$phoneConversations[_charId]>><<set $phoneConversations[_charId] = []>><</if>>
<<set _t = $timeSys || {}>>
<<set _time = { day: _t.day, month: _t.month, year: _t.year, hour: _t.hour, minute: _t.minute }>>
<<run $phoneConversations[_charId].push({ from: "player", text: _text, time: _time, read: true })>>
<<set _limit = 100>>
<<if $phoneConversations[_charId].length > _limit>>
<<run $phoneConversations[_charId].splice(0, $phoneConversations[_charId].length - _limit)>>
<</if>>
<</widget>>
<<widget "phoneMarkConversationRead">><<nobr>>
<<set _charId = $args[0]>>
<<if !_charId || !$phoneConversations[_charId]>><<return>><</if>>
<<run $phoneConversations[_charId].forEach(function(m){ m.read = true; })>>
<<if window.updatePhoneBadges>><<run window.updatePhoneBadges()>><</if>>
<</widget>>
<<widget "phoneStartConversation">><<nobr>>
<<set _charId = $args[0]>>
<<set _firstMsg = $args[1]>>
<<if !_charId>><<return>><</if>>
<<if !$phoneConversations>><<set $phoneConversations = {}>><</if>>
<<if !$phoneConversations[_charId]>><<set $phoneConversations[_charId] = []>><</if>>
<<if _firstMsg>>
<<phoneSendMessage _charId _firstMsg>>
<</if>>
<</widget>>
/* Ensure setup.phoneMessageTopics is loaded from variablesPhoneTopics passage. */
<<widget "phoneEnsureTopics">><<nobr>>
<<if !setup.phoneMessageTopics>>
<<set setup.phoneMessageTopics = {}>>
<</if>>
<</widget>>
<<widget "phoneUnlockContact">><<nobr>>
<<set _charId = $args[0]>>
<<if !_charId>><<return>><</if>>
<<if ndef $phoneContactsUnlocked>><<set $phoneContactsUnlocked = []>><</if>>
<<if !$phoneContactsUnlocked.includes(_charId)>>
<<run $phoneContactsUnlocked.push(_charId)>>
<</if>>
<</nobr>><</widget>>
/* ========== Where are you? – send player message + reply from schedule (no reply if sleeping/showering) ========== */
<<widget "phoneWhereAreYou">><<nobr>>
<<set _charId = $args[0]>>
<<if !_charId>><<return>><</if>>
<<phoneSendMessage _charId "Where are you?">>
<<updateCharacterLocations>>
<<set _char = $characters[_charId]>>
<<if !_char>><<return>><</if>>
<<set _status = _char.currentStatus || "">>
<<if _status === "sleeping">><<return>><</if>>
<<if _status === "showering">><<return>><</if>>
<<set _locId = _char.currentLocation || "">>
<<set _locName = "">>
<<if setup.navCards[_locId]>><<set _locName = setup.navCards[_locId].name>><</if>>
<<if !_locName>><<set _locName = _locId>><</if>>
<<if !_locName>><<set _locName = "home">><</if>>
<<set _reply = "I'm in the " + _locName + ".">>
<<phoneReceiveMessage _charId _reply>>
<</widget>>
/* ========== Calls (phone_system_data_and_technical.md §6) ========== */
<<widget "phoneCallLog">><<nobr>>
<<set _charId = $args[0]>>
<<set _dir = $args[1] || "out">>
<<if !_charId>><<return>><</if>>
<<if ndef $phoneCallsLog>><<set $phoneCallsLog = []>><</if>>
<<set _t = $timeSys || {}>>
<<set _time = { day: _t.day, month: _t.month, hour: _t.hour, minute: _t.minute }>>
<<run $phoneCallsLog.push({ charId: _charId, direction: _dir, time: _time })>>
<<set _max = 50>>
<<if $phoneCallsLog.length > _max>>
<<run $phoneCallsLog.splice(0, $phoneCallsLog.length - _max)>>
<</if>>
<</widget>>
<<widget "phoneCreateAppointment">><<nobr>>
<<set _charId = $args[0]>>
<<set _location = $args[1] || "">>
<<set _hour = $args[2]>>
<<set _minute = $args[3]>>
<<if !_charId>><<return>><</if>>
<<if ndef $phoneAppointments>><<set $phoneAppointments = []>><</if>>
<<set _t = $timeSys || {}>>
<<set _time = { day: _t.day, month: _t.month, hour: _hour, minute: _minute }>>
<<set _id = "apt_" + Date.now()>>
<<run $phoneAppointments.push({ id: _id, charId: _charId, time: _time, location: _location, status: "pending" })>>
<</widget>>
<<widget "phoneCancelAppointment">><<nobr>>
<<set _id = $args[0]>>
<<if !_id>><<return>><</if>>
<<run ($phoneAppointments || []).forEach(function(a,i,arr){ if(a.id === _id) arr[i].status = "cancelled"; })>>
<</widget>>
/* ========== Fotogram ========== */
<<widget "phoneFotogramPost">><<nobr>>
<<set _itemId = $args[0]>>
<<set _replaceMode = $args[1] === true || $args[1] === "true">>
<<if !_itemId>><<return>><</if>>
<<set State.temporary._fotogramItemId = _itemId>>
<<set State.temporary._fotogramReplace = _replaceMode>>
<<run (function(){
var itemId = State.temporary._fotogramItemId;
var replaceMode = State.temporary._fotogramReplace;
if (typeof window.phoneCreateFotogramPost === 'function') {
window.phoneCreateFotogramPost(itemId, replaceMode);
}
})()>>
<</nobr>><</widget>>
/* Call from advanceDay – updates likes, followers for Fotogram posts based on quality and days passed. */
<<widget "updateFotogramEngagement">><<nobr>>
<<run (function(){ if (typeof window.updateFotogramEngagement === 'function') window.updateFotogramEngagement(State.variables); })()>>
<</nobr>><</widget>>
/* Called from advanceTime when one or more hours pass. */
<<widget "updateFotogramEngagementHours">><<nobr>>
<<set _hours = parseInt($args[0], 10) || 0>>
<<if _hours > 0>>
<<set State.temporary._fgHours = _hours>>
<<run (function(){
var h = parseInt(State.temporary._fgHours, 10) || 0;
if (h > 0 && typeof window.updateFotogramEngagementHourly === 'function') {
window.updateFotogramEngagementHourly(State.variables, h);
}
})()>>
<</if>>
<</nobr>><</widget>>
/* Fotogram: Add notification (like, dm, comment). Usage: <<phoneFotogramNotify "dm" dmId>> */
<<widget "phoneFotogramNotify">><<nobr>>
<<set _type = $args[0]>>
<<set _refId = $args[1]>>
<<if !_type>><<return>><</if>>
<<if !$phoneNotifications>><<set $phoneNotifications = { fotogram: [], finder: [] }>><</if>>
<<if !$phoneNotifications.fotogram>><<set $phoneNotifications.fotogram = []>><</if>>
<<run $phoneNotifications.fotogram.push({ id: "fgnotif_" + Date.now(), type: _type, refId: _refId })>>
<<if window.updatePhoneBadges>><<run window.updatePhoneBadges()>><</if>>
<</nobr>><</widget>>
/* Fotogram DM: Send message (player reply). Usage: <<phoneFotogramDMSend dmId "text">> */
<<widget "phoneFotogramDMSend">><<nobr>>
<<set _dmId = $args[0]>>
<<set _text = $args[1] || "">>
<<if !_dmId || !_text>><<return>><</if>>
<<run (function(dmId, text){
if (!dmId || !text) return;
var vars = State.variables;
var dms = vars.phoneFotogramDMs || [];
var t = vars.timeSys || {};
var time = { day: t.day, month: t.month, year: t.year, hour: t.hour, minute: t.minute };
for (var i = 0; i < dms.length; i++) {
if (dms[i].id === dmId) {
dms[i].messages = dms[i].messages || [];
dms[i].messages.push({ from: "me", text: text, time: time, read: true });
if (window.persistPhoneChanges) window.persistPhoneChanges();
return;
}
}
})($args[0], $args[1] || "")>>
<</nobr>><</widget>>
/* Fotogram DM: Receive message from anon. Usage: <<phoneFotogramDMReceive dmId "text">> */
<<widget "phoneFotogramDMReceive">><<nobr>>
<<set _dmId = $args[0]>>
<<set _text = $args[1] || "">>
<<if !_dmId || !_text>><<return>><</if>>
<<run (function(dmId, text){
if (!dmId || !text) return;
var vars = State.variables;
var dms = vars.phoneFotogramDMs || [];
var t = vars.timeSys || {};
var time = { day: t.day, month: t.month, year: t.year, hour: t.hour, minute: t.minute };
for (var i = 0; i < dms.length; i++) {
if (dms[i].id === dmId) {
dms[i].messages = dms[i].messages || [];
dms[i].messages.push({ from: dms[i].anonId, text: text, time: time, read: false });
if (window.persistPhoneChanges) window.persistPhoneChanges();
if (window.updatePhoneBadges) window.updatePhoneBadges();
return;
}
}
})($args[0], $args[1] || "")>>
<</nobr>><</widget>>
/* Fotogram: Promote anon DM to real contact. Usage: <<phonePromoteContact dmId charId>> */
<<widget "phonePromoteContact">><<nobr>>
<<set _dmId = $args[0]>>
<<set _charId = $args[1]>>
<<if !_dmId>><<return>><</if>>
<<run (function(){
var dmId = State.temporary["_dmId"];
var charId = State.temporary["_charId"];
if (!dmId) return;
var maxSwap = (typeof window.PHONE_FOTOGRAM_RANDOM_SWAP_MAX !== "undefined" && Number.isFinite(window.PHONE_FOTOGRAM_RANDOM_SWAP_MAX)) ? window.PHONE_FOTOGRAM_RANDOM_SWAP_MAX : 10;
var vars = State.variables;
var dms = vars.phoneFotogramDMs || [];
vars.phoneFotogramRandomSwapIds = Array.isArray(vars.phoneFotogramRandomSwapIds) ? vars.phoneFotogramRandomSwapIds : [];
var isActiveFotogramRandom = function (id) {
if (!id) return false;
var def = vars.phoneGeneratedContacts && vars.phoneGeneratedContacts[id];
var st = vars.characters && vars.characters[id];
return !!(def && def.generatedFromPhone && st && st.firstMet);
};
vars.phoneFotogramRandomSwapIds = vars.phoneFotogramRandomSwapIds.filter(isActiveFotogramRandom).slice(0, maxSwap);
var canCreateFotogramRandom = function () {
return vars.phoneFotogramRandomSwapIds.length < maxSwap;
};
var markFotogramFirstMet = function (id) {
if (!id) return;
vars.characters = vars.characters || {};
if (!vars.characters[id]) return;
if (!vars.characters[id].firstMet) {
var ts = vars.timeSys || {};
vars.characters[id].firstMet = (ts.day || 1) + '/' + (ts.month || 1) + '/' + (ts.year || 0);
}
};
var buildSwapIntroMessage = function (contactId) {
var p = (vars.player && vars.player.firstName) ? vars.player.firstName : ((vars.characters && vars.characters.player && vars.characters.player.name) ? vars.characters.player.name : "Player");
var def = (vars.phoneGeneratedContacts && vars.phoneGeneratedContacts[contactId]) || ((typeof setup !== "undefined" && setup.characterDefs) ? setup.characterDefs[contactId] : null) || {};
var first = def.firstName || "";
var last = def.lastName || "";
var contactName = (first && last) ? (first + " " + last) : (def.name || contactId || "Unknown");
var currentYear = (vars.timeSys && Number(vars.timeSys.year)) || 2024;
var age = (def.birthYear && Number.isFinite(Number(def.birthYear))) ? (currentYear - Number(def.birthYear)) : 0;
if (!Number.isFinite(age) || age <= 0) age = 25;
var pool = (typeof setup !== "undefined" && Array.isArray(setup.fotogramSwapIntroMessages) && setup.fotogramSwapIntroMessages.length)
? setup.fotogramSwapIntroMessages
: [];
if (!pool.length) return "";
var tpl = pool[Math.floor(Math.random() * pool.length)] || "";
return String(tpl)
.replace(/\{playerName\}/g, p)
.replace(/\{contactName\}/g, contactName)
.replace(/\{age\}/g, String(age));
};
var dm = null;
for (var i = 0; i < dms.length; i++) { if (dms[i].id === dmId) { dm = dms[i]; break; } }
if (!dm) return;
var resolvedCharId = charId || null;
if (!resolvedCharId) resolvedCharId = dm.charId || (typeof setup !== "undefined" && setup.fotogramAnonCharPool && setup.fotogramAnonCharPool[dm.anonId]) || null;
if (!resolvedCharId) {
if (!canCreateFotogramRandom()) {
if (window.showNotification) window.showNotification({ type: 'warning', message: 'You can have at most ' + maxSwap + ' Fotogram swaps.' });
return;
}
if (typeof Engine !== "undefined" && Engine.wiki) Engine.wiki("\x3C\x3CcharGenerate \"" + dmId + "\"\x3E\x3E");
resolvedCharId = State.temporary["_generatedCharId"] || null;
/* If widget was not loaded yet (e.g. phone UI context), load passage and retry once. */
if (!resolvedCharId && typeof Engine !== "undefined" && Engine.wiki) {
Engine.wiki("\x3C\x3Cinclude \"CharGenerateWidget\"\x3E\x3E");
Engine.wiki("\x3C\x3CcharGenerate \"" + dmId + "\"\x3E\x3E");
resolvedCharId = State.temporary["_generatedCharId"] || null;
}
}
if (!resolvedCharId) {
if (window.showNotification) window.showNotification({ type: 'error', message: 'Could not create contact. Please try again.' });
return;
}
markFotogramFirstMet(resolvedCharId);
dm.promotedToCharId = resolvedCharId;
if (!vars.phoneContactPromoted) vars.phoneContactPromoted = {};
vars.phoneContactPromoted[dmId] = resolvedCharId;
if (typeof Engine !== "undefined" && Engine.wiki) Engine.wiki("\x3C\x3CphoneUnlockContact \"" + resolvedCharId + "\"\x3E\x3E");
var defResolved = vars.phoneGeneratedContacts && vars.phoneGeneratedContacts[resolvedCharId];
if (defResolved && defResolved.generatedFromPhone && vars.phoneFotogramRandomSwapIds.indexOf(resolvedCharId) === -1) {
if (vars.phoneFotogramRandomSwapIds.length < maxSwap) vars.phoneFotogramRandomSwapIds.push(resolvedCharId);
}
var conv = vars.phoneConversations || {};
if (!conv[resolvedCharId]) conv[resolvedCharId] = [];
var msgs = dm.messages || [];
for (var j = 0; j < msgs.length; j++) {
var m = msgs[j];
var from = m.from === "me" ? "player" : resolvedCharId;
conv[resolvedCharId].push({ from: from, text: m.text, time: m.time || {}, read: true });
}
if (!dm.swapIntroSent) {
var introText = buildSwapIntroMessage(resolvedCharId);
if (introText) {
var ts2 = vars.timeSys || {};
conv[resolvedCharId].push({
from: resolvedCharId,
text: introText,
time: { day: ts2.day, month: ts2.month, year: ts2.year, hour: ts2.hour, minute: ts2.minute },
read: false
});
dm.swapIntroSent = true;
}
}
if (window.persistPhoneChanges) window.persistPhoneChanges();
if (window.updatePhoneBadges) window.updatePhoneBadges();
})()>>
<</nobr>><</widget>>
/* Marks today's pending appointment with $interactingChar as completed. Call at end of meetup action scene. */
<<widget "completeMeetupAppointment">><<nobr>>
<<run (function(){
var charId = State.variables.interactingChar;
var list = State.variables.phoneAppointments || [];
var ts = State.variables.timeSys || {};
var today = (ts.year||0)*10000+(ts.month||0)*100+(ts.day||0);
for (var i = 0; i < list.length; i++) {
var a = list[i];
if (!a || a.status !== 'pending' || a.charId !== charId) continue;
var aptDate = (a.time.year||0)*10000+(a.time.month||0)*100+(a.time.day||0);
if (aptDate !== today) continue;
list[i].status = 'completed';
if (window.persistPhoneChanges) window.persistPhoneChanges();
break;
}
})()>>
<</widget>>
/* ========== Meetup prompt – show in passage when player is at meetup location at meetup time ========== */
/* Usage: <<meetupPrompt>> (uses current passage as location) or <<meetupPrompt "sunsetPark">> */
/* Uses State.variables._meetupPromptData (State.temp is not always extensible). */
<<widget "meetupPrompt">>
<<set _locId = $args[0] || State.passage>>
<<set State.variables._meetupPromptData = State.variables._meetupPromptData || {}>>
<<set State.variables._meetupPromptData.locId = _locId>>
<<run (function(){
var V = State.variables;
var data = V._meetupPromptData;
if (!data) return;
data.apt = null;
data.name = "";
data.aptId = null;
var list = V.phoneAppointments || [];
var ts = V.timeSys || {};
var currentDate = (ts.year || 0) * 10000 + (ts.month || 0) * 100 + (ts.day || 0);
var nowMin = (ts.hour || 0) * 60 + (ts.minute || 0);
var locId = data.locId;
var passage = State.passage;
for (var i = 0; i < list.length; i++) {
var a = list[i];
if (!a || a.status !== 'pending' || !a.time) continue;
var aptDate = (a.time.year || 0) * 10000 + (a.time.month || 0) * 100 + (a.time.day || 0);
if (aptDate !== currentDate) continue;
var aptMin = (a.time.hour || 0) * 60 + (a.time.minute || 0);
if (nowMin < aptMin || nowMin > aptMin + 30) continue;
var locMatch = (a.location === locId) || (setup.locations && setup.locations[passage] && setup.locations[passage].parent === locId);
if (!locMatch) continue;
data.apt = a;
data.aptId = a.id;
var ch = setup.getCharacter ? setup.getCharacter(a.charId) : null;
data.name = ch ? ((ch.firstName || ch.name || a.charId) + (ch.lastName ? " " + ch.lastName : "")) : a.charId;
return;
}
})()>>
<<set _data = State.variables._meetupPromptData>>
<<if !_data || !_data.apt>>
<<return>>
<</if>>
<div id="meetup-prompt-box" class="meetup-prompt">
<div class="meetup-prompt-text"><<print _data.name>> is here to meet you.</div>
<<link "Meet">>
<<run (function(){
var id = State.variables._meetupPromptData.aptId;
var list = State.variables.phoneAppointments || [];
for (var i = 0; i < list.length; i++) {
if (list[i] && list[i].id === id) { list[i].status = 'completed'; break; }
}
if (window.persistPhoneChanges) window.persistPhoneChanges();
})()>>
<<replace "#meetup-prompt-box">><div class="meetup-prompt-done">You met with <<print State.variables._meetupPromptData.name>>!</div><</replace>>
<</link>>
</div>
<</widget>>
/* ==========================================
QUEST SYSTEM V2 - DATABASE BASE
Initializes setup.quests object
========================================== */
/* Initialize empty quest database */
<<script>>
setup.quests = {};
<</script>>
/*
Quest Structure Reference:
setup.quests.questId = {
id: "questId",
title: "Quest Title",
category: "main" | "side" | "daily" | "event",
character: "charId" | null,
/* Optional: journal shows this under the title while the quest is active (pacing, where to go). */
tip: "Short player-facing hint string",
requirements: {
flags: ["flag1", "flag2"],
quests: ["prevQuestId"],
relationships: { mother: { love: 30 } },
stats: { confidence: 50 }
},
stages: [
{
id: "stage1",
title: "Stage Title",
desc: "Stage description",
tip: "Optional per-stage hint (journal, same row as stage desc)",
triggers: {
location: "locationId",
time: { period: "morning" | "afternoon" | "evening" | "night", hour: [8, 12] },
character: { id: "charId", status: "available" },
flag: "someFlag"
},
requirements: { ... }, // Stage-specific requirements
forceScene: true | false, // Auto-redirect to passage
passage: "passageName", // Target passage for forceScene
objectives: [
{ id: "obj1", text: "Do something", trigger: { flag: "didIt" } }
],
completeWhen: "allObjectives" | "anyObjective",
onTrigger: {
advanceStage: true // Auto-advance without scene
}
}
],
onComplete: {
relationships: { mother: { love: 10, friendship: 5 } },
stats: { charisma: 5 },
money: 100,
flags: ["questComplete"],
notification: "Custom completion message",
nextQuest: "nextQuestId"
}
}
*/
/* ==========================================
QUEST SYSTEM V2 - MAIN STORY QUESTS
Main storyline quests (character arcs)
Note: All quest scenes use manual button navigation.
Triggers are only used for showing quest prompts
at specific locations (not auto-navigation).
========================================== */
<<script>>
Object.assign(setup.quests, {
/* ========== FIRST SHOPPING - MAP DISCOVERY ========== */
first_shopping: {
id: "first_shopping",
title: "First Shopping",
category: "tutorial",
character: "mother",
requirements: {
flags: ["prologue_complete"]
},
stages: [
{
id: "go_to_shop",
title: "Go to the Shop",
desc: "Head to the Corner Shop to buy groceries for mom",
triggers: { location: "storeCorner" },
items: [
{ objectiveId: "milk", itemId: "quest_milk", location: "storeCorner" },
{ objectiveId: "bread", itemId: "quest_bread", location: "storeCorner" }
],
objectives: [
{ id: "milk", text: "Buy milk" },
{ id: "bread", text: "Buy bread" }
],
completeWhen: "allObjectives"
},
{
id: "map_discovery",
title: "Discover the City Map",
desc: "You notice something interesting on the counter...",
buttonText: "City Map... What's this?",
triggers: { location: "storeCorner" },
forceScene: true,
passage: "quest_first_shopping_map_scene"
}
],
onComplete: {
flags: ["first_shopping_done", "met_marcus"],
notification: "You got a free city map!",
nextQuest: "moving_troubles"
}
},
/* ========== MOVING TROUBLES - BAD NEWS CHAIN ========== */
moving_troubles: {
id: "moving_troubles",
title: "Moving Troubles",
category: "tutorial",
character: null,
requirements: {
quests: ["first_shopping"]
},
stages: [
{
id: "walking_home",
title: "Walking Home",
desc: "Head back home with the groceries",
buttonText: "You hear sounds in the distance...",
triggers: { location: "maplewood" },
forceScene: true,
passage: "quest_moving_troubles_sounds"
},
{
id: "talk_to_mother",
title: "Talk to Mother",
desc: "Give mom the groceries",
/* Manual: Button in sounds scene goes to fhKitchen,
then quest button appears */
buttonText: "Talk to your mother",
triggers: { location: "fhKitchen", character: { id: "mother" } },
forceScene: true,
passage: "quest_moving_troubles_mother_talk"
},
{
id: "fathers_news",
title: "Father's News",
desc: "Listen to what father has to say",
/* Manual: Button in mother_talk scene goes directly to this passage */
passage: "quest_moving_troubles_father_news"
},
{
id: "go_to_room",
title: "Go to Your Room",
desc: "You need some time alone",
/* Manual: Button in father_news scene goes directly to this passage */
passage: "quest_moving_troubles_room_scene"
}
],
onComplete: {
flags: ["moving_troubles_done", "belongings_lost"],
notification: "Quest Complete: Moving Troubles",
nextQuest: "new_beginnings"
}
},
/* ========== NEW BEGINNINGS - FAMILY SUPPORT ========== */
new_beginnings: {
id: "new_beginnings",
title: "New Beginnings",
category: "family",
character: "player",
mealType: "dinner", // Integrates with family meal widget
requirements: {
quests: ["moving_troubles"]
},
stages: [
{
id: "family_dinner",
title: "Family Dinner",
desc: "Join your family for dinner at 18:00",
buttonText: "Eat with Family",
triggers: { location: "fhKitchen" },
forceScene: true,
passage: "quest_new_beginnings_dinner"
}
],
onComplete: {
flags: ["new_beginnings_done", "got_allowance"],
rewards: { money: 100 },
notification: "Quest Complete: New Beginnings",
nextQuest: ["use_computer", "find_job"]
}
},
/* ========== USE COMPUTER - EXPLORE ONLINE ========== */
use_computer: {
id: "use_computer",
title: "Use Brother's Computer",
category: "tutorial",
character: "player",
requirements: {
quests: ["new_beginnings"]
},
stages: [
{
id: "start_computer",
title: "Use the Computer",
desc: "Use your brother's computer to explore online",
buttonText: "Use Computer",
triggers: { location: "fhBrotherRoom" },
forceScene: true,
passage: "quest_use_computer_01_start"
},
{
id: "browse_districts",
title: "Browse City Districts",
desc: "Learning about the city neighborhoods",
passage: "quest_use_computer_02_district_map"
},
{
id: "check_career_center",
title: "Check Career Center",
desc: "Found information about Town Hall Career Services",
passage: "quest_use_computer_03_career_center"
},
{
id: "brother_interrupts",
title: "Brother Returns",
desc: "Your brother wants to use his computer",
passage: "quest_use_computer_04_brother_enters"
}
],
onComplete: {
flags: ["used_computer", "learned_oldtown", "learned_civic_center", "knows_corner_shop_hiring"],
notification: "Quest Complete: Use Brother's Computer"
}
},
/* ========== FIND JOB - EMPLOYMENT SEARCH ========== */
find_job: {
id: "find_job",
title: "Find a Job",
category: "main",
character: "player",
/* No mealType: Corner Shop / Civic Center etc. show any time; dinner stage uses triggers.timeRange */
requirements: {
quests: ["new_beginnings"]
},
stages: [
{
id: "start_job_search",
title: "Start Job Search",
desc: "Look for employment opportunities in the city"
},
{
id: "check_corner_shop",
title: "Check Corner Shop",
desc: "Your brother mentioned the Corner Shop is hiring",
buttonText: "Ask About the Job",
triggers: { location: "storeCorner" },
forceScene: true,
passage: "quest_find_job_corner_shop"
},
{
id: "check_civic_center",
title: "Check Civic Center",
desc: "Marcus suggested looking for jobs at Town Hall, Civic Center",
tip: "Check Old Town between 09:00 and 17:00.",
buttonText: "Find Town Hall",
triggers: {
location: "oldTown",
timeRange: { min: 9, max: 17 }
},
lockedText: "Town Hall is closed. (Open 9:00 - 17:00)",
forceScene: true,
passage: "quest_find_job_civic_center"
},
{
id: "enter_town_hall",
title: "Enter Town Hall",
desc: "Step inside Town Hall to find the Career Services office",
buttonText: "Find Career Services",
triggers: {
location: "townHall",
timeRange: { min: 9, max: 17 }
},
forceScene: true,
passage: "quest_find_job_town_hall_entrance"
},
{
id: "find_career_services",
title: "Find Career Services",
desc: "Ask at the information desk for directions",
passage: "quest_find_job_town_hall_information"
},
{
id: "wait_in_queue",
title: "Wait in Queue",
desc: "There's a long line at Career Services",
passage: "quest_find_job_career_services_queue"
},
{
id: "meet_with_clerk",
title: "Meet with Clerk",
desc: "Speak with the Career Services clerk",
passage: "quest_find_job_career_services_kiosk"
},
{
id: "ruby_diner_offer",
title: "Job Opportunity Found",
desc: "The clerk found a waitress position at Ruby's Diner",
passage: "quest_find_job_ruby_diner_offer"
},
{
id: "ruby_diner_interview",
title: "Apply at Ruby's Diner",
desc: "You have the address. Go to Ruby's Diner and apply for the job.",
buttonText: "Ask about the job",
triggers: {
location: "dinerRubys",
timeRange: { min: 10, max: 22 }
},
lockedText: "Ruby's Diner is closed. (Open 10:00 - 22:00)",
forceScene: true,
passage: "quest_find_job_ruby_diner_entrance"
},
{
id: "family_dinner_18",
title: "Family Dinner (18:00)",
desc: "Join your family for dinner at 18:00",
buttonText: "Dinner",
triggers: {
location: "fhKitchen",
timeRange: { min: 18, max: 19 }
},
lockedText: "Family dinner is at 18:00. Come back then.",
forceScene: true,
passage: "quest_find_job_family_dinner"
},
{
id: "family_dinner_dishes",
title: "After Dinner",
desc: "Confessed to Mom about Ruby's.",
passage: "quest_find_job_family_dinner_dishes"
},
{
id: "accept_job",
title: "Accept the Job",
desc: "Return to Ruby's Diner and accept the dishwashing position.",
buttonText: "Accept the job",
triggers: { location: "dinerRubys" },
forceScene: true,
passage: "quest_find_job_accept_entrance"
}
],
onComplete: {
flags: ["job_search_started", "knows_civic_center_jobs"],
notification: "Quest Complete: Find a Job"
}
},
/* ========== GO TO MALL - Lily's suggestion ========== */
go_to_mall: {
id: "go_to_mall",
title: "Go To Mall",
category: "side",
character: "parkRunnerLily",
requirements: {},
stages: [
{
id: "visit_downtown",
title: "Visit Downtown",
desc: "Lily pointed you to Metro Shopping Center. Head to Downtown and find the mall.",
buttonText: "Go to Downtown",
triggers: { location: "downTown" },
forceScene: true,
passage: "quest_go_to_mall_downtown_first"
},
{
id: "visit_mall",
title: "Explore the Mall",
desc: "You've found the mall. Take a look around inside.",
buttonText: "Enter the Mall",
triggers: { location: "mall" },
forceScene: true,
passage: "quest_go_to_mall_mall_first"
}
],
onComplete: {
flags: ["go_to_mall_done"],
notification: "Quest Complete: Go To Mall"
}
},
/* ========== LILY GYM INTRO ========== */
lily_gym_intro: {
id: "lily_gym_intro",
title: "Try the Gym",
category: "side",
character: "parkRunnerLily",
requirements: {},
stages: [
{
id: "visit_iron_works_gym",
title: "Check Gym",
desc: "Lily suggested training at Iron Works Gym. Go there and check it out yourself.",
tip: "The gym is located in Apex Tower, in Downtown's Skyline District.",
triggers: { location: "gym" }
},
{
id: "talk_to_reception",
title: "Talk to Reception",
desc: "Say hello at the front desk and ask about the gym.",
buttonText: "Talk at the front desk",
tip: "When you enter Iron Works Gym, check the front desk.",
triggers: { location: "gym" },
forceScene: true,
passage: "quest_lily_gym_frontdesk_intro"
}
],
onComplete: {
flags: ["lily_gym_intro_done"],
notification: "Quest Complete: Try the Gym"
}
},
/* ========== CHECK OLD TOWN ========== */
check_old_town: {
id: "check_old_town",
title: "Check Old Town",
category: "side",
character: "player",
requirements: {
flags: ["mom_suggested_mall"]
},
stages: [
{
id: "check_old_town",
title: "Check Old Town",
desc: "Look around Old Town and find the pharmacy and barber shop.",
buttonText: "Check Old Town",
triggers: { location: "oldTown" },
forceScene: true,
passage: "quest_check_old_town"
}
],
onComplete: {
notification: "Quest Complete: Check Old Town"
}
},
/* ========== BROTHER BROWSER RABBIT HOLE ========== */
brother_browser_rabbit_hole: {
id: "brother_browser_rabbit_hole",
title: "What He Watches",
category: "side",
character: "brother",
tip: "Browse from your brother's computer when he is not around, then come back tomorrow and check again.",
requirements: {},
stages: [
{
id: "check_history",
title: "Check the History",
desc: "Use your brother's computer and see what else is in the browser history.",
tip: "Browse from your brother's computer when he is not around, then come back tomorrow and check again."
},
{
id: "look_again",
title: "Look Again",
desc: "There is more in the history than the first thing you found.",
tip: "Come back another day, when the excuse feels easier."
},
{
id: "dig_deeper",
title: "Dig Deeper",
desc: "The searches are starting to form a pattern.",
tip: "Use Browse / Surf again on another day."
},
{
id: "one_more_search",
title: "One More Search",
desc: "One more look should tell you what he keeps coming back to.",
tip: "Check the history one last time."
}
],
onComplete: {
flags: ["brother_browser_rabbit_hole_done"],
notification: "Quest Complete: What He Watches"
}
},
/* ========== SOMETHING DIFFERENT - DIANA / BEAUTY ARC ========== */
something_different: {
id: "something_different",
title: "Something Different",
category: "main",
character: "player",
requirements: {
flags: ["dianaGossipUnlocked"]
},
stages: [
{
id: "talk_to_girls",
title: "Talk to the Girls",
desc: "Emma and Sofia seemed to know something. Talk to both of them at the diner.",
triggers: { location: "dinerRubys" },
objectives: [
{ id: "talkEmma", text: "Talk to Emma about Diana" },
{ id: "talkSofia", text: "Talk to Sofia about Diana" }
],
completeWhen: "allObjectives"
},
{
id: "mirror_moment",
title: "Think About It",
desc: "Go to your room and think about it.",
triggers: { location: "fhBedroom" }
},
{
id: "research",
title: "Do Some Research",
desc: "Use your brother's computer.",
buttonText: "Search online",
triggers: { location: "fhBrotherRoomPC" },
forceScene: true,
passage: "brotherComputer_beautySearch"
},
{
id: "talk_mom",
title: "Talk to Mom",
desc: "Go talk to your mother when she on the bed.",
tip: "Talk to Mom in the parents' room between 23:00 and 24:00.",
buttonText: "Ask mom about skincare",
triggers: {
location: "fhParentsRoom",
character: { id: "mother" },
timeRange: { min: 23, max: 24 }
},
lockedText: "Come back later tonight.",
forceScene: true,
passage: "fhParentsRoom_event_motherTalk"
},
{
id: "ask_dad",
title: "Ask Dad for Money",
desc: "Dad's still awake downstairs. Talk to him."
},
{
id: "visit_mall",
title: "Check the Mall",
desc: "Go to the mall and browse around.",
buttonText: "Explore the mall",
triggers: { location: "mall" },
forceScene: true,
passage: "mall_event_beautyVisit"
},
{
id: "after_mall",
title: "Sit With It",
desc: "Go back to your room.",
triggers: { location: "fhBedroom" }
},
{
id: "find_money",
title: "Find a Way",
desc: "Late at night, Leave the bedroom and go upstairs.",
tip: "Go to the upstairs hallway between 23:00 and 24:00.",
triggers: {
location: "fhUpperstairs",
timeRange: { min: 23, max: 24 }
},
lockedText: "Wait until the house settles down tonight.",
forceScene: true,
passage: "fhUpperstairs_event_walletChance"
}
],
onComplete: {
flags: ["something_different_done"],
notification: "Quest Complete: Something Different"
}
},
/* ========== VINCE DAY 3, FAMILY (mirror / self-image) ========== */
vince_day3_family: {
id: "vince_day3_family",
title: "Unfinished Thoughts",
category: "main",
character: "player",
requirements: {
flags: ["vinceInspectionDay3Shown"]
},
stages: [
{
id: "reach_out",
title: "Speak with your family",
desc: "Something's been eating at you since work.",
objectives: [
{ id: "talkMother", text: "Speak with your mother in the kitchen" },
{ id: "talkBrother", text: "Speak with your brother in his room" },
{ id: "talkFather", text: "Speak with your father in the garage" }
],
completeWhen: "allObjectives"
},
{
id: "bedroom_alone",
title: "Think About It",
desc: "You need think about that in your room.",
triggers: { location: "fhBedroom" }
}
],
onComplete: {
flags: ["vince_day3_family_quest_done"],
notification: "Quest complete: Family"
}
}
});
<</script>>
/* ==========================================
QUEST ITEMS DATABASE
Items that appear only during quests
========================================== */
<<run Object.assign(setup.items, {
questItems: [
{
id: "quest_milk",
name: "Fresh Milk",
category: "grocery",
usageType: "quest",
desc: "A carton of fresh milk. Mom asked you to buy this.",
price: 3,
image: "assets/content/items/groceries/milk.webp",
hasTooltip: true
},
{
id: "quest_bread",
name: "Bread",
category: "grocery",
usageType: "quest",
desc: "A loaf of fresh bread. Mom needs this for the family.",
price: 2,
image: "assets/content/items/groceries/bread.webp",
hasTooltip: true
},
]
})>>/* ==========================================
QUEST SYSTEM V2 - STATE INITIALIZATION
Initializes $questState for quest tracking
========================================== */
/* Initialize quest state tracking */
<<set $questState = {
active: {}, // { questId: { stage: 0, objectives: {}, triggeredStage: -1, startDate: "1/1/2025" } }
completed: [], // ["questId1", "questId2", ...]
failed: [], // ["questId3", ...]
/* Reserved for repeatable/daily quest bookkeeping not wired in macros yet. */
daily: {},
/* Set on <<completeQuest>>: { questId: YYYYMMDD } in storyJavaScript.js */
completedDates: {}
}>>
/* Current quest context (used during forced scenes) */
<<set $currentQuest = null>>
/* ==========================================
UTILITY WIDGETS
Game log and certificates
NOTE: Quest macros moved to story_javascript.js
Use: <<startQuest>>, <<advanceQuestStage>>,
<<completeObjective>>, <<completeQuest>>
========================================== */
/*
<<addLog "Title" "Description" "Icon">>
Adds an entry to the game log.
Icon defaults to "bookmark" if not provided.
*/
<<widget "addLog">>
<<set _title = $args[0]>>
<<set _desc = $args[1]>>
<<set _icon = $args[2] || "bookmark">>
<<run $gameLog.unshift({
title: _title,
desc: _desc,
icon: _icon,
date: $timeSys ? (months[$timeSys.month-1] + " " + $timeSys.day) : "Day 1"
})>>
<</widget>>
/*
<<addCertificate "Name" "Date">>
Adds a certificate.
*/
<<widget "addCertificate">>
<<set _name = $args[0]>>
<<set _date = $args[1] || "Unknown Date">>
<<run $certificates.push({
name: _name,
date: _date
})>>
<<notify "success" ("Certificate Earned: " + _name)>>
<</widget>><<nobr>>
<<set _backTo = $readReturnPassage || "fhUpperBath">>
<<set $location = setup.resolveReadingLocationId(_backTo)>>
<<readingScreen _backTo>>
<</nobr>>/* ==================================================
READ WIDGETS
Redirect to Read passage and store return passage
Usage: <<readBtn>>
================================================== */
<<widget "readBtn">>
<<btn "Read" "Read">>
<<set $readReturnPassage = State.passage>>
<</btn>>
<</widget>>
<<script>>
/* =====================================================================
READING DATABASE
BOOK SCHEMA:
{
id: "book_example", // unique key, must match ItemDatabase id
name: "Example Title",
pages: 180, // 120–350
statPool: ["intelligence","focus","willpower"],
// stats this book can award; readExecute picks 1–2 per session
statGainPerPage: 0.02, // raw gain = pagesRead × statGainPerPage
// split randomly between 1–2 stats from statPool; no cap
requires: { // optional; omit for beginner books (no lock)
intelligence: 15, // minimum INT to read
focus: 10 // minimum FOC to read
},
skillOnRead: [ // optional array; omit for everyday/stat-only books
{ category: "social", skill: "persuasion", gainPerPage: 0.025 }, // primary
{ category: "social", skill: "networking", gainPerPage: 0.012 } // secondary (large books only)
],
earlyNarratives: [ // pages 0–33% opening, setting, introduction (3–4 entries)
"...",
],
midNarratives: [ // pages 33–66% rising action, deeper themes (3–4 entries)
"...",
],
lateNarratives: [ // pages 66–100% resolution, reflection, closing (3–4 entries)
"...",
]
}
MAGAZINE SCHEMA:
single use; first issue of the day: gainOnComplete and/or skillOnComplete.
===================================================================== */
setup.readingItems = {
books: [
// ═══════════════════════════════════════════════════
// PERSUASION SERIES
// ═══════════════════════════════════════════════════
{
id: "book_quiet_pitch",
name: "The Quiet Pitch",
pages: 185,
statPool: ["charisma", "intelligence"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "social", skill: "persuasion", gainPerPage: 0.025 }
],
earlyNarratives: [
"The opening chapters reframe persuasion as listening, not talking. You make a note to stop arguing and start asking.",
"A simple framework about framing your ask around the other person's interests actually clicks for you.",
"The first exercise asks you to recall a failed conversation and spot where you lost the thread. You find it easily."
],
midNarratives: [
"Case studies show how small word choices shift outcomes. You start noticing the same patterns in your own conversations.",
"The book introduces the 'quiet pitch' making people feel like the choice was theirs. It feels almost too simple.",
"A chapter on timing makes you rethink when you bring things up, not just how."
],
lateNarratives: [
"The final chapters bring it together: confidence isn't loudness, it's clarity. You feel more prepared.",
"You finish a worked example and realize you've already started using these techniques without noticing.",
"The last section is about knowing when not to push. That part sticks with you most."
]
},
{
id: "book_never_back_down",
name: "Never Back Down",
pages: 275,
requires: { intelligence: 15, focus: 10 },
statPool: ["charisma", "intelligence", "willpower"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "social", skill: "persuasion", gainPerPage: 0.025 }
],
earlyNarratives: [
"The opening dives straight into high-stakes negotiation salaries, contracts, difficult conversations. The examples feel real.",
"A chapter on anchoring makes you realize how often you've undersold yourself by speaking first.",
"The book distinguishes negotiation from conflict. That distinction alone reframes how you think about disagreement."
],
midNarratives: [
"The pressure scenarios are uncomfortable to read because you recognize your own patterns in them.",
"A section on reading body language during negotiation gives you specific cues to watch for.",
"You work through a sample negotiation exercise. You're better at this than you expected."
],
lateNarratives: [
"The chapter on walking away knowing your exit point might be the most powerful thing in the book.",
"Advanced positioning techniques feel almost tactical. You start mapping them onto situations you've already been in.",
"The final case study follows a negotiation from start to finish. You follow every move and understand why each worked."
]
},
{
id: "book_power_of_words",
name: "The Power of Words",
pages: 400,
requires: { intelligence: 25, focus: 20 },
statPool: ["charisma", "intelligence", "willpower"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "social", skill: "persuasion", gainPerPage: 0.025 },
{ category: "social", skill: "conversation", gainPerPage: 0.012 }
],
earlyNarratives: [
"The introduction reframes language as architecture building spaces people want to enter. Dense, but worth it.",
"Linguistic pattern analysis in the first section is slower going than expected, but the examples from speeches are fascinating.",
"A breakdown of how charismatic speakers structure their sentences makes you read your own messages differently."
],
midNarratives: [
"Corporate and political case studies show how the same techniques play out at scale. The parallels to everyday life are useful.",
"A chapter on subtext what's not said is the most interesting thing you've read in weeks.",
"The book introduces persuasion and conversation as one continuous skill. You start to see how they bleed together."
],
lateNarratives: [
"Synthesizing everything feels overwhelming at first, then suddenly clear. You stop thinking of persuasion as a trick.",
"The final chapters walk through authentic influence leading without manipulating. It requires a lot of self-awareness.",
"You close the last page having filled the margins with more notes than any book in a while."
]
},
// ═══════════════════════════════════════════════════
// CONVERSATION SERIES
// ═══════════════════════════════════════════════════
{
id: "book_just_talk",
name: "Just Talk",
pages: 175,
statPool: ["charisma", "focus"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "social", skill: "conversation", gainPerPage: 0.025 }
],
earlyNarratives: [
"The first chapters cover the basics how to open, how to keep going, how to exit. Obvious in theory, harder in practice.",
"A list of genuine questions for small talk feels almost too practical. You memorize three of them.",
"The book's core idea: most people are waiting to talk, not listening to respond. You catch yourself doing it immediately."
],
midNarratives: [
"A section on follow-up questions using what someone just said to ask the next changes how you listen.",
"The chapter on silences reframes awkward pauses as moments of trust, not failure. That shifts something for you.",
"Case dialogues show the difference between surface talk and actual connection. The gap is smaller than you thought."
],
lateNarratives: [
"Wrapping up conversations well turns out to be its own skill. You hadn't thought about exits before.",
"The last section covers lasting impressions how people remember how you made them feel, not what you said.",
"You finish the book and have three conversations the same day. All of them feel a little smoother."
]
},
{
id: "book_social_blueprint",
name: "The Social Blueprint",
pages: 260,
requires: { intelligence: 15, focus: 10 },
statPool: ["charisma", "intelligence", "focus"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "social", skill: "conversation", gainPerPage: 0.025 }
],
earlyNarratives: [
"The book opens on group dynamics how conversations shift when there are three people instead of two. Immediately more complex.",
"A breakdown of social hierarchies in casual settings is uncomfortably accurate.",
"The first chapters cover reading energy in a room knowing when to speak and when to wait."
],
midNarratives: [
"Adapting your style to different people not performing, just adjusting clicks into place here.",
"Examples of social navigation in professional settings feel directly applicable. You take notes.",
"A chapter on what undermines trust filler words, hedging, over-explaining makes you cringe at your own habits."
],
lateNarratives: [
"Long-term relationship building turns out to be a different skill from initial conversation. The book explains both.",
"A section on consistency being the same person in different rooms is harder than it sounds.",
"You finish with a clearer picture of how you show up socially. Some of it's good. Some isn't."
]
},
{
id: "book_mastering_connection",
name: "Mastering Connection",
pages: 380,
requires: { intelligence: 25, focus: 20 },
statPool: ["charisma", "intelligence", "focus"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "social", skill: "conversation", gainPerPage: 0.025 },
{ category: "social", skill: "networking", gainPerPage: 0.012 }
],
earlyNarratives: [
"The opening reframes connection as a practice, not a talent. That's both relieving and demanding.",
"Deep listening not waiting for your turn is harder to describe than to do. The book shows how.",
"A chapter on vulnerability in conversation. Not oversharing, but being genuinely present. A fine line."
],
midNarratives: [
"Cross-cultural communication takes up an entire section how context, silence, and eye contact mean different things.",
"The book introduces conversational investment what you bring to an exchange, not just what you take.",
"A case study follows two conversations one transactional, one genuine and dissects exactly where they diverged."
],
lateNarratives: [
"Authentic self-expression comes up in the final chapters. Not performance actual presence. That's harder work.",
"Charisma, the book argues, is what happens when you stop trying to be interesting and start being interested.",
"You close the last chapter feeling like the book asked more of you than expected. That's probably the point."
]
},
// ═══════════════════════════════════════════════════
// RESEARCH SERIES
// ═══════════════════════════════════════════════════
{
id: "book_how_to_find_anything",
name: "How to Find Anything",
pages: 180,
statPool: ["intelligence", "focus"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "mental", skill: "research", gainPerPage: 0.025 }
],
earlyNarratives: [
"The first chapters cover source evaluation how to tell reliable from unreliable. Simpler criteria than expected.",
"A breakdown of search strategies beyond keyword matching changes how you approach finding information.",
"The book opens with a challenge: trace a common belief back to its actual source. It takes longer than you thought."
],
midNarratives: [
"Organizing information not just collecting it turns out to be where most people fall apart. You recognize that in yourself.",
"A section on tracking your research trail so you can rebuild your thinking later. You start doing it immediately.",
"The chapter on primary vs. secondary sources makes you rethink half the things you already think you know."
],
lateNarratives: [
"Presenting findings clearly knowing what to include and what to cut is its own discipline. The book covers it well.",
"A worked example traces a full research process from question to conclusion. Watching it unfold is satisfying.",
"You finish with a clearer method for approaching any topic you don't understand yet."
]
},
{
id: "book_source_and_truth",
name: "Source & Truth",
pages: 290,
requires: { intelligence: 15, focus: 12 },
statPool: ["intelligence", "focus"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "mental", skill: "research", gainPerPage: 0.025 }
],
earlyNarratives: [
"The media literacy section opens with examples that should be obvious but aren't. Bias hides in structure, not just content.",
"A chapter on how misinformation spreads not through lies but through selective truth is uncomfortable to read.",
"The book introduces triangulating sources. Cross-referencing becomes second nature by chapter four."
],
midNarratives: [
"An entire section on confirmation bias in research. You read it twice because the first time you were agreeing too easily.",
"A framework for evaluating expert consensus vs. fringe opinion. More nuanced than 'trust the majority.'",
"A chapter on building a research system personal notes, source tracking makes you rethink how you organize things."
],
lateNarratives: [
"The final section builds a personal research philosophy. Less method, more mindset.",
"A case study tracks a false story from origin to widespread acceptance. Reading it is clarifying and alarming.",
"You finish with a sharper instinct for what to trust and why. More importantly, what to question."
]
},
{
id: "book_information_age",
name: "The Information Age",
pages: 420,
requires: { intelligence: 25, focus: 20 },
statPool: ["intelligence", "focus", "willpower"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "mental", skill: "research", gainPerPage: 0.025 },
{ category: "mental", skill: "analysis", gainPerPage: 0.012 }
],
earlyNarratives: [
"Information architecture how knowledge is structured is the opening thesis. It reframes what research actually is.",
"A section on data patterns and what they reveal versus what they obscure. Heavy going, but worth it.",
"The book traces how information systems have evolved over time. History as methodology."
],
midNarratives: [
"Advanced analytical frameworks show up mid-book. Comparing models, stress-testing assumptions, identifying gaps.",
"A chapter on synthesizing contradictory data not choosing a side, but holding both is genuinely difficult.",
"The reading slows down here. The material is dense. You take breaks and come back."
],
lateNarratives: [
"Synthesizing complex topics not summarizing, actually integrating takes a different kind of attention.",
"The final chapters feel academic. That's a compliment. Your notes are three pages long by the end.",
"You close the book less certain than when you started, but with a much better understanding of why."
]
},
// ═══════════════════════════════════════════════════
// PROBLEM SOLVING SERIES
// ═══════════════════════════════════════════════════
{
id: "book_think_it_through",
name: "Think It Through",
pages: 195,
statPool: ["intelligence", "focus"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "mental", skill: "problemSolving", gainPerPage: 0.025 }
],
earlyNarratives: [
"The first exercise asks you to write down a problem you're avoiding. Then define it in one sentence. Harder than expected.",
"Root cause analysis asking 'why' five times feels simple until you try it on something real.",
"A framework for separating symptoms from causes changes how you look at frustrating situations."
],
midNarratives: [
"Brainstorming frameworks not just listing ideas, but structured generation make the process feel less random.",
"A section on constraint identification: knowing what's actually fixed versus what just feels fixed.",
"You work through a sample problem with the book's method. It takes longer than improvising, but the result is cleaner."
],
lateNarratives: [
"Decision matrices for evaluating options. More useful than pros-and-cons lists. You've already started using one.",
"Testing solutions before committing minimal versions, quick feedback is obvious in hindsight.",
"The final chapter reframes failure as data. Not new, but the way it's explained here actually lands."
]
},
{
id: "book_lateral_mind",
name: "The Lateral Mind",
pages: 265,
requires: { intelligence: 15, focus: 12 },
statPool: ["intelligence", "focus", "willpower"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "mental", skill: "problemSolving", gainPerPage: 0.025 }
],
earlyNarratives: [
"Lateral thinking approaching problems from an unexpected angle is harder to practice than to understand.",
"The opening chapters challenge assumptions you didn't know you were making. Each one stings a little.",
"A section on reframing problems: describing them differently until a new solution becomes visible."
],
midNarratives: [
"A series of lateral thinking exercises. Some feel like tricks. Some feel like genuine insight.",
"A chapter on challenging constraints questioning every 'must' and 'can't' in a problem statement.",
"The examples of lateral solutions to classic problems make you want to try the method on something difficult."
],
lateNarratives: [
"Innovative solution patterns categories of non-obvious answers give you a vocabulary for new ideas.",
"The book argues that creativity in problem-solving is a practice, not a talent. Reassuring and demanding.",
"You finish with a few techniques you plan to actually use. That's more than most books."
]
},
{
id: "book_mind_unlocked",
name: "Mind Unlocked",
pages: 390,
requires: { intelligence: 25, focus: 20 },
statPool: ["intelligence", "focus", "willpower"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "mental", skill: "problemSolving", gainPerPage: 0.025 },
{ category: "mental", skill: "research", gainPerPage: 0.012 }
],
earlyNarratives: [
"Systems thinking seeing problems as networks of causes, not isolated events is the opening framework. Dense from page one.",
"Complex problem mapping requires drawing diagrams. You actually do it. The mess on paper is clarifying.",
"A section on feedback loops and unintended consequences. Problems don't exist in isolation."
],
midNarratives: [
"Multi-variable analysis: holding more than one cause in mind without collapsing to the simplest explanation.",
"A chapter on uncertainty making good decisions without enough information. The book is honest that there's no formula.",
"The reading is slow here. You reread sections. That's probably the point."
],
lateNarratives: [
"Integrating intuition with logic when to trust your instinct and when to override it is the most honest part.",
"The final case studies involve messy, real-world problems. No clean answers. Just better thinking.",
"You close the book with a different relationship to hard problems. Less dread, more process."
]
},
// ═══════════════════════════════════════════════════
// GAMING SERIES
// ═══════════════════════════════════════════════════
{
id: "book_level_up",
name: "Level Up",
pages: 190,
statPool: ["focus", "intelligence"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "technical", skill: "gaming", gainPerPage: 0.025 }
],
earlyNarratives: [
"The book opens with game theory basics zero-sum vs. positive-sum, dominant strategies. Surprisingly applicable outside games.",
"A breakdown of what separates skilled from lucky play. More systematic than you expected.",
"The meta-game concept playing to the current environment, not some ideal makes you rethink your approach."
],
midNarratives: [
"A section on decision-making under time pressure. The book calls it 'fast good' versus 'slow perfect.'",
"Resource management principles apply across genres. You start thinking about them outside games too.",
"Positioning controlling space and options is a whole chapter. Abstract enough to be useful anywhere."
],
lateNarratives: [
"The final chapters cover adaptability reading shifts in the game state and adjusting rather than committing.",
"A section on tilt emotional interference with performance. The book is specific about how to manage it.",
"You finish the book having made a few connections to situations outside gaming. That feels right."
]
},
{
id: "book_pro_gamer_mindset",
name: "Pro Gamer Mindset",
pages: 280,
requires: { intelligence: 15, focus: 15 },
statPool: ["focus", "intelligence", "willpower"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "technical", skill: "gaming", gainPerPage: 0.025 }
],
earlyNarratives: [
"Competitive psychology opens the book. The difference between players who peak early and those who improve steadily.",
"Tilt control maintaining performance when things go wrong is framed as a skill, not a personality trait. That helps.",
"A section on reading opponents: pattern recognition across matches, not just reactions in the moment."
],
midNarratives: [
"Adaptation mid-match updating your strategy when your plan fails requires a kind of real-time humility.",
"A chapter on the difference between mechanics and game sense. High execution skill doesn't automatically transfer to decision-making.",
"Pattern recognition across multiple sessions. The book teaches a way of reviewing your own play without ego."
],
lateNarratives: [
"Team dynamics communication, roles, trust under pressure take up the final third of the book.",
"Shotcalling: making decisions for a group when information is incomplete. High-stakes version of everyday leadership.",
"You finish thinking about competitive play differently. The mental game is deeper than you assumed."
]
},
{
id: "book_competitive_edge",
name: "The Competitive Edge",
pages: 350,
requires: { intelligence: 25, focus: 25 },
statPool: ["focus", "intelligence", "willpower"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "technical", skill: "gaming", gainPerPage: 0.025 },
{ category: "mental", skill: "problemSolving", gainPerPage: 0.012 }
],
earlyNarratives: [
"High-level strategy reading your opponent's read on you is the opening thesis. It goes several layers deep quickly.",
"A section on game states and how to identify turning points in real time. Theoretical but very precise.",
"The book assumes you've already built the basics. It doesn't re-explain them. You appreciate the respect."
],
midNarratives: [
"Mental fortitude in extended competitive play maintaining quality when fatigue and frustration compound.",
"A chapter on opponent modeling: building a mental picture of how someone thinks, then finding the gaps.",
"The reading is dense mid-book. You take it slower. The examples reward the attention."
],
lateNarratives: [
"Mastery mindset the relationship between practice, plateau, and breakthrough is the most interesting section.",
"Continuous improvement as a system: review, adjust, test. Structured but not mechanical.",
"You close the book feeling like the gap between where you are and high-level play is clearer. That's useful."
]
},
// ═══════════════════════════════════════════════════
// COOKING SERIES
// ═══════════════════════════════════════════════════
{
id: "book_from_scratch",
name: "From Scratch",
pages: 200,
statPool: ["creativity", "focus"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "practical", skill: "cooking", gainPerPage: 0.025 }
],
earlyNarratives: [
"The opening chapters cover kitchen setup and mise en place having everything ready before you start. Simple, but transformative.",
"Knife skills get more attention than expected. Precision in prep makes cooking less stressful, apparently.",
"A section on heat management. High heat isn't always better. Understanding the difference changes everything."
],
midNarratives: [
"Basic techniques: sauté, roast, boil, simmer. The book explains why each works, not just how.",
"A chapter on seasoning at different stages of cooking. Salt at the end is a myth this book dispels.",
"You follow along mentally with a recipe and realize you're starting to understand the logic behind each step."
],
lateNarratives: [
"Building flavors layering acid, fat, salt, heat comes together in the final chapters. It's actually coherent now.",
"Simple plating principles. You don't need to make it beautiful, just considered.",
"You close the book with a list of things you actually want to try cooking. That feels like success."
]
},
{
id: "book_kitchen_manual",
name: "The Kitchen Manual",
pages: 285,
requires: { intelligence: 12, focus: 10 },
statPool: ["creativity", "focus", "intelligence"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "practical", skill: "cooking", gainPerPage: 0.025 }
],
earlyNarratives: [
"The book opens on ingredients understanding their properties deeply, not just how they taste.",
"A section on the Maillard reaction and caramelization. Why things brown. Why it matters. Suddenly cooking has chemistry.",
"Flavor profiles and how to balance them: sweet, sour, salty, bitter, umami. Each has a role."
],
midNarratives: [
"Complex techniques: braising, emulsification, reduction. The book walks through each with enough detail to actually follow.",
"A chapter on pan sauces building a sauce from the fond left in the pan. You didn't know this was something you could do.",
"Resting meat, blooming spices, deglazing small habits that accumulate into better cooking."
],
lateNarratives: [
"Menu planning how to design a meal so everything finishes at the same time. Harder to learn from a book than from practice, but the logic starts to click.",
"Timing multiple dishes without panic. The book calls it 'sequencing.' You call it useful.",
"You finish with enough theory to start improvising. That feels like the actual goal."
]
},
{
id: "book_mastery_kitchen",
name: "Mastery in the Kitchen",
pages: 410,
requires: { intelligence: 20, focus: 18 },
statPool: ["creativity", "focus", "intelligence"],
statGainPerPage: 0.02,
skillOnRead: [
{ category: "practical", skill: "cooking", gainPerPage: 0.025 },
{ category: "practical", skill: "finance", gainPerPage: 0.010 }
],
earlyNarratives: [
"Flavor science takes up the first section why certain combinations work at a chemical level. The most technical cooking book you've read.",
"A chapter on texture contrast, temperature variation, and how the senses interact while eating.",
"The opening sets an ambitious standard: cooking as craft, not just sustenance."
],
midNarratives: [
"Restaurant-quality execution at home requires different habits more than different equipment.",
"A section on food costing and waste reduction. Cooking well and cooking economically aren't opposites.",
"The book covers building a signature style returning to certain flavors and techniques that feel like yours."
],
lateNarratives: [
"Creating original recipes starting from an idea rather than a template requires all the theory the book has built to this point.",
"A chapter on teaching others: explaining techniques, common mistakes, how to troubleshoot.",
"You finish the book with a sense that cooking has become something else. Not a task a practice."
]
},
// ═══════════════════════════════════════════════════
// EVERYDAY BOOKS (stat-only)
// ═══════════════════════════════════════════════════
{
id: "book_stillwater",
name: "Stillwater",
pages: 180,
statPool: ["willpower", "focus", "intelligence"],
statGainPerPage: 0.02,
earlyNarratives: [
"The opening chapters introduce stoic principles through small, practical examples. It reads like a conversation, not a lecture.",
"A section on the dichotomy of control distinguishing what you influence from what you don't. You underline most of it.",
"Present-moment thinking isn't meditation, the book argues. It's just paying attention. Simple to say."
],
midNarratives: [
"Applying philosophy to daily stressors: traffic, small failures, people who frustrate you. The exercises are quiet but effective.",
"A chapter on equanimity not suppressing emotions but not being controlled by them. The line is narrow.",
"The middle section addresses ambition: how to want things without being ruled by wanting them."
],
lateNarratives: [
"Living with intention is the final theme. Not rigid planning just knowing why you're doing what you're doing.",
"A section on peace of mind as something practiced, not achieved. That reframing is genuinely helpful.",
"You close the book slowly. It seems to reward that."
]
},
{
id: "book_letters_never_sent",
name: "Letters Never Sent",
pages: 195,
statPool: ["creativity", "willpower"],
statGainPerPage: 0.02,
earlyNarratives: [
"The protagonist's first letters are careful, restrained you sense what's being held back more than what's written.",
"The writing style is quiet and precise. It takes a few chapters to settle into.",
"Early relationships are sketched in light strokes. You're curious where they're going."
],
midNarratives: [
"The conflicts deepen mid-book. What started as a quiet correspondence becomes something more complicated.",
"A relationship you thought you understood shifts. You go back and reread an earlier letter.",
"The protagonist starts withholding more. The unsent letters become the most revealing part."
],
lateNarratives: [
"Resolution comes slowly. Not everything is resolved. That feels honest.",
"The final letters are shorter. More certain. Something has settled, even if it's not happiness.",
"You finish and sit with it for a moment. A good book asks something of you. This one did."
]
},
{
id: "book_curious_mind",
name: "The Curious Mind",
pages: 250,
requires: { intelligence: 15 },
statPool: ["intelligence", "focus"],
statGainPerPage: 0.02,
earlyNarratives: [
"The opening covers everyday physics why things fall, float, or stay still. You've never thought about them this way.",
"A chapter on probability and how poorly human intuition handles it. Uncomfortable but clarifying.",
"The book's premise: wonder isn't childish. Not knowing is a starting point, not a failure."
],
midNarratives: [
"The biology of habits and decision-making. Why you do things you don't mean to, and how that can change.",
"A section on how the brain builds models of the world, and what happens when those models are wrong.",
"The reading gets denser mid-book. You slow down and take notes."
],
lateNarratives: [
"The final section covers where the interesting scientific questions are now. The edge of what's known.",
"A chapter on not knowing: the value of holding questions open rather than forcing answers.",
"You finish the book more curious than when you started. That seems like the whole point."
]
}
],
magazines: [
{
id: "mag_think_quick",
name: "Think Quick",
pages: 36,
gainOnComplete: 1,
gainTypeOptions: ["intelligence", "focus"],
skillMaxCap: 30,
skillOnComplete: { category: "mental", skill: "problemSolving", amount: 1 },
narratives: [
"You work through puzzle-style scenarios and compare multiple solutions.",
"A short problem-solving framework actually helps you break things down.",
"You read case examples and try to predict outcomes before the page does."
],
skimNarratives: [
"You flip through challenge pages without solving much.",
"You skim the diagrams and move on.",
"You browse quickly, mostly passing time."
]
},
{
id: "mag_small_talk",
name: "Small Talk Weekly",
pages: 34,
gainType: "charisma",
gainOnComplete: 1,
skillMaxCap: 30,
skillOnComplete: { category: "social", skill: "conversation", amount: 1 },
narratives: [
"You read practical examples for smoother everyday conversations.",
"A section on listening and follow-up questions gives you new ideas.",
"You review common awkward moments and how to handle them naturally."
],
skimNarratives: [
"You skim the tips without really taking notes.",
"You glance at conversation examples and move on.",
"You read headlines only and let the pages pass."
]
},
{
id: "mag_home_flavors",
name: "Home Flavors",
pages: 40,
skillMaxCap: 30,
skillOnComplete: { category: "practical", skill: "cooking", amount: 1 },
narratives: [
"You read simple recipe techniques and timing tricks.",
"A prep guide teaches you how to avoid common kitchen mistakes.",
"You compare seasoning notes and cooking order for better results."
],
skimNarratives: [
"You flip through food photos more than instructions.",
"You skim recipe titles and skip the details.",
"You browse quickly without learning much."
]
},
{
id: "mag_social_edge",
name: "Social Edge",
pages: 32,
skillMaxCap: 30,
skillOnComplete: { category: "social", skill: "persuasion", amount: 1 },
narratives: [
"You read examples about framing ideas more convincingly.",
"A section on tone and timing makes your arguments feel cleaner.",
"You study negotiation snippets and pick up useful patterns."
],
skimNarratives: [
"You skim the advice columns without focusing deeply.",
"You glance at negotiation examples and move along.",
"You read just enough to pass time."
]
},
{
id: "mag_green_corner",
name: "Green Corner",
pages: 38,
skillMaxCap: 30,
skillOnComplete: { category: "practical", skill: "gardening", amount: 1 },
narratives: [
"You read beginner guides on soil, light, and watering balance.",
"A plant care chart helps you understand seasonal routines.",
"You pick up practical tips on keeping small plants healthy."
],
skimNarratives: [
"You flip through plant photos and short captions.",
"You skim care charts without memorizing much.",
"You browse quickly, mostly for visuals."
]
},
{
id: "mag_makeup_studio",
name: "Makeup Studio",
pages: 35,
gainType: "mood",
gainOnComplete: 1,
skillMaxCap: 30,
skillOnComplete: { category: "practical", skill: "makeup", amount: 1 },
narratives: [
"You read blending and shade-matching tips step by step.",
"A lookbook explains fast everyday makeup routines.",
"You compare product order and finish styles for cleaner results."
],
skimNarratives: [
"You skim the looks without trying to learn the techniques.",
"You flip pages for inspiration more than instruction.",
"You browse quickly and close the issue."
]
},
{
id: "mag_creative_pulse",
name: "Creative Pulse",
pages: 34,
gainType: "creativity",
gainOnComplete: 1,
narratives: [
"You study bold layouts and color choices that spark new ideas.",
"A short feature on visual style gives you fresh inspiration.",
"You read a creative breakdown and start seeing patterns differently."
],
skimNarratives: [
"You flip through visuals without really processing them.",
"You skim headlines and move on quickly.",
"You browse for a moment, then lose focus."
]
},
{
id: "mag_inner_drive",
name: "Inner Drive",
pages: 33,
gainType: "willpower",
gainOnComplete: 1,
narratives: [
"You read habit-building tips and feel more grounded.",
"A discipline-focused article helps you frame your goals clearly.",
"You go through practical routines for staying consistent."
],
skimNarratives: [
"You skim motivation quotes without reflecting much.",
"You read a few lines, then drift away.",
"You flip pages quickly and close it."
]
}
]
};
<</script>><<script>>
/* =====================================================================
READING SYSTEM - Config (readingLocations) + logic (helpers, speed).
Data: setup.readingItems from ReadingDatabase[init].
===================================================================== */
/* Sub-passages that share the same reading spot as a parent room (intro + videos). */
setup.resolveReadingLocationId = function (passageId) {
var map = {
fhCouch: "fhLivingroom",
fhBed: "fhBedroom"
};
return map[passageId] || passageId;
};
/* Location-based reading: intro + book.media / magazine.media per passage id (must match $readReturnPassage / passage name). */
var _livingRoomReading = {
intro: "You curl up on the couch and open your book.",
book: {
media: [
{ type: "vid", path: "assets/content/scenes/global/read/readBookLivingroom1.mp4", size: "100%" },
{ type: "vid", path: "assets/content/scenes/global/read/readBookLivingroom2.mp4", size: "100%" }
]
},
magazine: {
media: [
{ type: "vid", path: "assets/content/scenes/global/read/readMagazineLivingroom1.mp4", size: "100%" }
]
}
};
setup.readingLocations = {
fhBedroom: {
intro: "You sit on the bed and get comfortable with your book.",
book: {
media: [
{ type: "vid", path: "assets/content/scenes/global/read/readBookBed1.mp4", size: "100%" }
]
},
magazine: {
media: [
{ type: "vid", path: "assets/content/scenes/global/read/readMagazinebed1.mp4", size: "100%" },
{ type: "vid", path: "assets/content/scenes/global/read/readMagazinebed2.mp4", size: "100%" }
]
}
},
fhLivingroom: _livingRoomReading,
fhLivingRoom: _livingRoomReading,
default: {
intro: "You find a quiet spot and start reading.",
book: { media: [] },
magazine: { media: [] }
}
};
/* Helper: get reading item by id */
setup.getReadingItem = function(id) {
for (let b of (setup.readingItems.books || [])) {
if (b.id === id) return { ...b, type: "book" };
}
for (let m of (setup.readingItems.magazines || [])) {
if (m.id === id) return { ...m, type: "magazine" };
}
return null;
};
/* Helper: base 1 pg/min. Stats additively raise speed.
INT +0.4%/pt, FOCUS +0.3%/pt, WIL +0.2%/pt → max ~1.9x at 100/100/100 */
setup.calcReadingSpeed = function(intelligence, focus, willpower) {
return 1
+ (intelligence || 0) * 0.004
+ (focus || 0) * 0.003
+ (willpower || 0) * 0.002;
};
setup.getReadingLocationIntro = function(passageId) {
var id = setup.resolveReadingLocationId(passageId);
var loc = setup.readingLocations[id] || setup.readingLocations.default;
return (typeof loc === "string") ? loc : loc.intro;
};
/* Returns one random media item for the location and item type (book/magazine), or null if none. */
setup.getReadingLocationMediaRandom = function(passageId, itemType) {
var id = setup.resolveReadingLocationId(passageId);
var loc = setup.readingLocations[id] || setup.readingLocations.default;
if (typeof loc === "string") return null;
var media = [];
if (itemType === "book" && loc.book && loc.book.media && loc.book.media.length) media = loc.book.media;
else if (itemType === "magazine" && loc.magazine && loc.magazine.media && loc.magazine.media.length) media = loc.magazine.media;
else if (loc.media && loc.media.length) media = loc.media;
if (media.length === 0) return null;
return media[Math.floor(Math.random() * media.length)];
};
<</script>><<nobr>>
/* ==========================================
RELATIONSHIP DECAY WIDGETS
Tracks when characters were last seen and
slowly decays friendship while unseen.
========================================== */
/* Mark a character as seen today. */
/* Usage: <<touchCharacterRelation "mother">> */
<<widget "touchCharacterRelation">>
<<set _charId = $args[0]>>
<<if !_charId || !$characters || !$characters[_charId]>><<return>><</if>>
<<run (function () {
var V = State.variables;
var charId = State.temporary._charId;
var charState = V.characters && V.characters[charId];
var ts = V.timeSys || {};
if (!charState) return;
var year = Number(ts.year || 0);
var month = Number(ts.month || 1);
var day = Number(ts.day || 1);
var dayNumber = Math.floor(Date.UTC(year, month - 1, day) / 86400000);
charState.lastSeenAt = { year: year, month: month, day: day };
charState.lastSeenDayNumber = dayNumber;
if (typeof charState.lastRelationshipDecayDayNumber !== "number") {
charState.lastRelationshipDecayDayNumber = dayNumber;
}
})()>>
<</widget>>
/* Apply relationship decay once per day. */
/* Hidden effect: no toast spam for every NPC at day rollover. */
<<widget "applyRelationshipDecay">>
<<if !$gameSettings.relationshipDecay>><<return>><</if>>
<<run (function () {
var V = State.variables;
var chars = V.characters || {};
var ts = V.timeSys || {};
var year = Number(ts.year || 0);
var month = Number(ts.month || 1);
var day = Number(ts.day || 1);
var currentDayNumber = Math.floor(Date.UTC(year, month - 1, day) / 86400000);
Object.keys(chars).forEach(function (charId) {
if (charId === "player") return;
var charState = chars[charId];
if (!charState || charState.known !== true || !charState.stats) return;
if (typeof charState.lastSeenDayNumber !== "number") {
charState.lastSeenDayNumber = currentDayNumber;
charState.lastRelationshipDecayDayNumber = currentDayNumber;
return;
}
var lastSeen = Number(charState.lastSeenDayNumber);
var lastDecay = (typeof charState.lastRelationshipDecayDayNumber === "number")
? Number(charState.lastRelationshipDecayDayNumber)
: lastSeen;
var unseenDays = currentDayNumber - lastSeen;
if (unseenDays < 7) return;
var decayAnchor = Math.max(lastSeen, lastDecay);
var daysSinceDecay = currentDayNumber - decayAnchor;
if (daysSinceDecay < 3) return;
var decaySteps = Math.floor(daysSinceDecay / 3);
if (decaySteps <= 0) return;
var currentFriendship = Number(charState.stats.friendship || 0);
if (currentFriendship <= 0) {
charState.lastRelationshipDecayDayNumber = currentDayNumber;
return;
}
charState.stats.friendship = Math.max(0, currentFriendship - decaySteps);
charState.lastRelationshipDecayDayNumber = decayAnchor + (decaySteps * 3);
});
})()>>
<</widget>>
<</nobr>>
/* ==========================================
REPUTATION SYSTEM - Data Structure
========================================== */
/* Region connections for rumor spreading */
<<set setup.regionConnections = {
"home": ["maplewood"],
"maplewood": ["home", "downtown", "oldTown", "sunsetPark"],
"downtown": ["maplewood", "hillcrest", "redLightCenter", "universityDistrict"],
"hillcrest": ["downtown", "marinaBay"],
"marinaBay": ["hillcrest", "downtown"],
"oldTown": ["maplewood", "southside", "universityDistrict"],
"universityDistrict": ["oldTown", "downtown", "suburbs"],
"southside": ["oldTown", "suburbs", "redLightCenter"],
"suburbs": ["southside", "universityDistrict"],
"redLightCenter": ["downtown", "southside"]
}>>
/* Reputation category definitions */
<<set setup.reputationCategories = {
"athlete": { name: "Athlete", isGlobal: false, color: "#22c55e" },
"model": { name: "Model", isGlobal: true, color: "#a855f7" },
"camgirl": { name: "Camgirl", isGlobal: true, color: "#ec4899" },
"stripper": { name: "Stripper", isGlobal: false, color: "#f59e0b" },
"escort": { name: "Escort", isGlobal: false, color: "#ef4444" },
"porn": { name: "Porn", isGlobal: true, color: "#ef4444" },
"socialMedia": { name: "Social Media", isGlobal: true, color: "#3b82f6" }
}>>
/* Tier definitions based on overall score */
<<set setup.reputationTiers = [
{ min: 0, max: 12, id: "pure", name: "Pure", color: "#4ade80" },
{ min: 13, max: 25, id: "good", name: "Good Girl", color: "#60a5fa" },
{ min: 26, max: 40, id: "flirty", name: "Flirty", color: "#f472b6" },
{ min: 41, max: 55, id: "normal", name: "Normal", color: "#9ca3af" },
{ min: 56, max: 70, id: "known", name: "Known", color: "#fbbf24" },
{ min: 71, max: 85, id: "easy", name: "Easy", color: "#fb923c" },
{ min: 86, max: 95, id: "slut", name: "Slut", color: "#f87171" },
{ min: 96, max: 100, id: "prostitution", name: "Prostitution", color: "#ff6b6b" }
]>>
/* Region display info */
<<set setup.regionInfo = {
"home": { name: "Home", subtitle: "Family House" },
"maplewood": { name: "Maplewood", subtitle: "Residential" },
"downtown": { name: "Downtown", subtitle: "Business District" },
"hillcrest": { name: "Hillcrest", subtitle: "Upscale Area" },
"marinaBay": { name: "Marina Bay", subtitle: "Waterfront" },
"oldTown": { name: "Old Town", subtitle: "Historic District" },
"universityDistrict": { name: "University", subtitle: "Campus" },
"southside": { name: "Southside", subtitle: "Working Class" },
"suburbs": { name: "Suburbs", subtitle: "Outskirts" },
"redLightCenter": { name: "Red Light", subtitle: "Adult District" }
}>>
/* Initialize reputation data - all regions start with empty scores */
<<set $reputation = {
"home": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"maplewood": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"downtown": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"hillcrest": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"marinaBay": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"oldTown": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"universityDistrict": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"southside": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"suburbs": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 },
"redLightCenter": { athlete: 0, model: 0, camgirl: 0, stripper: 0, escort: 0, porn: 0, socialMedia: 0 }
}>>
/* Global reputation values (shared across all regions for internet categories) */
<<set $globalReputation = {
model: 0,
camgirl: 0,
porn: 0,
socialMedia: 0
}>>/* ==========================================
REPUTATION SYSTEM - Widgets
========================================== */
/* ----------------------------------------
<<gainReputation regionId category amount>>
Gains reputation in a specific region/category
Handles global categories and rumor spreading
---------------------------------------- */
<<widget "gainReputation">>
<<set _region = _args[0]>>
<<set _category = _args[1]>>
<<set _amount = _args[2] || 1>>
<<if !$reputation[_region]>>
/* unknown region */
<<else>>
/* Check if this is a global category */
<<set _catInfo = setup.reputationCategories[_category]>>
<<if _catInfo && _catInfo.isGlobal>>
/* Global category - update everywhere */
<<set $globalReputation[_category] = Math.min(100, ($globalReputation[_category] || 0) + _amount)>>
/* Sync to all regions */
<<for _r range Object.keys($reputation)>>
<<set $reputation[_r][_category] = $globalReputation[_category]>>
<</for>>
<<else>>
/* Local category - update specific region */
<<set $reputation[_region][_category] = Math.min(100, ($reputation[_region][_category] || 0) + _amount)>>
/* Spread rumors to connected regions (50% decay) */
<<set _connections = setup.regionConnections[_region] || []>>
<<for _connectedRegion range _connections>>
<<if $reputation[_connectedRegion]>>
<<set _spreadAmount = Math.floor(_amount * 0.5)>>
<<if _spreadAmount > 0>>
<<set $reputation[_connectedRegion][_category] = Math.min(100, ($reputation[_connectedRegion][_category] || 0) + _spreadAmount)>>
<</if>>
<</if>>
<</for>>
<</if>>
<</if>>
<</widget>>
/* ----------------------------------------
<<loseReputation regionId category amount>>
Loses reputation in a specific region/category
---------------------------------------- */
<<widget "loseReputation">>
<<set _region = _args[0]>>
<<set _category = _args[1]>>
<<set _amount = _args[2] || 1>>
<<if $reputation[_region]>>
<<set _catInfo = setup.reputationCategories[_category]>>
<<if _catInfo && _catInfo.isGlobal>>
/* Global - decrease everywhere */
<<set $globalReputation[_category] = Math.max(0, ($globalReputation[_category] || 0) - _amount)>>
<<for _r range Object.keys($reputation)>>
<<set $reputation[_r][_category] = $globalReputation[_category]>>
<</for>>
<<else>>
/* Local only */
<<set $reputation[_region][_category] = Math.max(0, ($reputation[_region][_category] || 0) - _amount)>>
<</if>>
<</if>>
<</widget>>
/* ----------------------------------------
<<getRegionOverall regionId>>
Returns overall reputation score for a region
Sets _return with the score (0-100)
---------------------------------------- */
<<widget "getRegionOverall">>
<<set _region = _args[0]>>
<<set _return = 0>>
<<if $reputation[_region]>>
<<set _total = 0>>
<<set _count = 0>>
<<for _cat range Object.keys($reputation[_region])>>
<<set _total += $reputation[_region][_cat] || 0>>
<<set _count++>>
<</for>>
<<set _return = _count > 0 ? Math.round(_total / _count) : 0>>
<</if>>
<</widget>>
/* ----------------------------------------
<<getReputationTier score>>
Returns tier info based on score
Sets _return with tier object {id, name, color}
---------------------------------------- */
<<widget "getReputationTier">>
<<set _score = _args[0] || 0>>
<<set _return = { id: "pure", name: "Pure", color: "#4ade80" }>>
<<for _tier range setup.reputationTiers>>
<<if _score >= _tier.min && _score <= _tier.max>>
<<set _return = _tier>>
<<break>>
<</if>>
<</for>>
<</widget>>
/* ----------------------------------------
<<syncGlobalReputation>>
Syncs global reputation values to all regions
Call this after loading a save or on game start
---------------------------------------- */
<<widget "syncGlobalReputation">>
<<for _cat range Object.keys($globalReputation)>>
<<for _region range Object.keys($reputation)>>
<<set $reputation[_region][_cat] = $globalReputation[_cat]>>
<</for>>
<</for>>
<</widget>>
/* ==========================================
CHARACTER OPINION WIDGETS
========================================== */
/* <<gainOpinion "characterId" amount>>
Increases a character's awareness/opinion about player */
<<widget "gainOpinion">>
<<set _charId = _args[0]>>
<<set _amount = _args[1] || 5>>
<<if $characters[_charId]>>
<<if !$characters[_charId].opinion>>
<<set $characters[_charId].opinion = { awareness: 0, flags: [] }>>
<</if>>
<<set $characters[_charId].opinion.awareness = Math.min(100, $characters[_charId].opinion.awareness + _amount)>>
<</if>>
<</widget>>
/* <<loseOpinion "characterId" amount>>
Decreases character's awareness */
<<widget "loseOpinion">>
<<set _charId = _args[0]>>
<<set _amount = _args[1] || 5>>
<<if $characters[_charId] && $characters[_charId].opinion>>
<<set $characters[_charId].opinion.awareness = Math.max(0, $characters[_charId].opinion.awareness - _amount)>>
<</if>>
<</widget>>
/* <<setOpinionFlag "characterId" "flagName">>
Sets a specific awareness flag (e.g., "sawProof", "caughtSneaking") */
<<widget "setOpinionFlag">>
<<set _charId = _args[0]>>
<<set _flag = _args[1]>>
<<if $characters[_charId]>>
<<if !$characters[_charId].opinion>>
<<set $characters[_charId].opinion = { awareness: 0, flags: [] }>>
<</if>>
<<if !$characters[_charId].opinion.flags.includes(_flag)>>
<<run $characters[_charId].opinion.flags.push(_flag)>>
<</if>>
<</if>>
<</widget>>
/* <<hasOpinionFlag "characterId" "flagName">>
Returns true if flag is set, stores in _return */
<<widget "hasOpinionFlag">>
<<set _charId = _args[0]>>
<<set _flag = _args[1]>>
<<set _return = false>>
<<if $characters[_charId]?.opinion?.flags>>
<<set _return = $characters[_charId].opinion.flags.includes(_flag)>>
<</if>>
<</widget>>
/* <<getOpinionTier "characterId">>
Returns tier info, stores in _return */
<<widget "getOpinionTier">>
<<set _charId = _args[0]>>
<<set _awareness = $characters[_charId]?.opinion?.awareness || 0>>
<<set _return = { id: "innocent", name: "Innocent", color: "#4ade80", desc: "Thinks you're a good girl" }>>
<<for _tier range setup.opinionTiers>>
<<if _awareness >= _tier.min && _awareness <= _tier.max>>
<<set _return = _tier>>
<<break>>
<</if>>
<</for>>
<</widget>><<script>>
/* =====================================================================
RESTAURANT DATABASE
- setup.restaurantDishes: { foods: [...], drinks: [...] }
- setup.restaurantMenus[menuId]: { name, foods, drinks, back? }
back = return passage (else $location)
- Dish: id, name, price, desc?, image?, calories?, effects: [{ stat, value, type }]
calories = added to $dailyCalorieIntake when $gameMenutings.trackCalories (foods & drinks).
NOT related to setup.items (shopping). Used only by restaurant system.
EFFECTS BY ITEM (value = change: negative = reduce, positive = gain)
-----------------------------------------------------------------------
FOODS (Ruby's Diner):
- House Salad hunger -25, energy +5
- Soup of the Day hunger -20, mood +3
- Chili hunger -30, energy +8, mood +3
- Ruby's Classic Burger hunger -45, energy +15
- BLT Sandwich hunger -38, energy +10
- Grilled Cheese hunger -35, mood +3
- Reuben Sandwich hunger -42, energy +12
- Fried Chicken Plate hunger -52, energy +18
- Meatloaf hunger -48, energy +15, mood +5
- Club Sandwich hunger -48, energy +14
- Mac & Cheese hunger -38, mood +4
- Short Stack Pancakes hunger -40, energy +12
- French Toast hunger -35, energy +10, mood +4
- Waffles hunger -38, energy +10, mood +3
- Eggs & Bacon hunger -42, energy +18
- Omelette hunger -40, energy +15
- Pasta Carbonara hunger -45, energy +12
- Grilled Salmon hunger -50, energy +15, mood +5
- Steak & Frites hunger -55, energy +20
- Apple Pie hunger -20, mood +8, stress -5
FOOD COURT SETS (main + fries + drink included):
- Smash Burger Menu hunger -60, thirst -35, energy +20
- Crispy Chicken Menu hunger -58, thirst -35, energy +20
- Pizza Menu hunger -55, thirst -35, energy +18, mood +3
- Burrito Bowl Menu hunger -58, thirst -38, energy +18
- Hot Dog Menu hunger -50, thirst -35, energy +15
- Veggie Wrap Menu hunger -48, thirst -35, energy +15, mood +4
DRINKS:
- Water thirst -40
- Coffee energy +25
- Iced Tea thirst -35, energy +5
- Soda thirst -25
- Fresh Juice thirst -30, energy +8
- Lemonade thirst -35, mood +3
- Hot Chocolate thirst -20, mood +6, stress -5
- Milkshake thirst -40, mood +5
===================================================================== */
setup.restaurantDishes = {
foods: [
{
id: "house_salad",
name: "House Salad",
price: 8,
calories: 150,
desc: "Fresh greens with vinaigrette.",
image: "assets/content/items/restaurants/house_salad.webp",
effects: [
{ stat: "hunger", value: -25, type: "instant" },
{ stat: "energy", value: 5, type: "instant" }
]
},
{
id: "soup_of_day",
name: "Soup of the Day",
price: 8,
calories: 120,
desc: "Chef's daily selection.",
image: "assets/content/items/restaurants/soup_of_day.webp",
effects: [
{ stat: "hunger", value: -20, type: "instant" },
{ stat: "mood", value: 3, type: "instant" }
]
},
{
id: "diner_burger",
name: "Ruby's Classic Burger",
price: 10,
calories: 550,
desc: "Beef patty, lettuce, tomato, fries on the side.",
image: "assets/content/items/restaurants/diner_burger.webp",
effects: [
{ stat: "hunger", value: -45, type: "instant" },
{ stat: "energy", value: 15, type: "instant" }
]
},
{
id: "grilled_cheese",
name: "Grilled Cheese",
price: 8,
calories: 400,
desc: "Toasted cheese sandwich, side of tomato soup.",
image: "assets/content/items/restaurants/grilled_cheese.webp",
effects: [
{ stat: "hunger", value: -35, type: "instant" },
{ stat: "mood", value: 3, type: "instant" }
]
},
{
id: "pancakes",
name: "Short Stack Pancakes",
price: 9,
calories: 450,
desc: "Fluffy pancakes with butter and syrup.",
image: "assets/content/items/restaurants/pancakes.webp",
effects: [
{ stat: "hunger", value: -40, type: "instant" },
{ stat: "energy", value: 12, type: "instant" }
]
},
{
id: "club_sandwich",
name: "Club Sandwich",
price: 11,
calories: 550,
desc: "Triple-decker, turkey, bacon, lettuce, tomato.",
image: "assets/content/items/restaurants/club_sandwich.webp",
effects: [
{ stat: "hunger", value: -48, type: "instant" },
{ stat: "energy", value: 14, type: "instant" }
]
},
{
id: "mac_cheese",
name: "Mac & Cheese",
price: 8,
calories: 450,
desc: "Creamy macaroni and cheese, comfort classic.",
image: "assets/content/items/restaurants/mac_cheese.webp",
effects: [
{ stat: "hunger", value: -38, type: "instant" },
{ stat: "mood", value: 4, type: "instant" }
]
},
{
id: "eggs_bacon",
name: "Eggs & Bacon",
price: 9,
calories: 400,
desc: "Two eggs any style, crispy bacon, toast.",
image: "assets/content/items/restaurants/eggs_bacon.webp",
effects: [
{ stat: "hunger", value: -42, type: "instant" },
{ stat: "energy", value: 18, type: "instant" }
]
},
{
id: "pasta_carbonara",
name: "Pasta Carbonara",
price: 12,
calories: 650,
desc: "Creamy bacon and parmesan.",
image: "assets/content/items/restaurants/pasta_carbonara.webp",
effects: [
{ stat: "hunger", value: -45, type: "instant" },
{ stat: "energy", value: 12, type: "instant" }
]
},
{
id: "grilled_salmon",
name: "Grilled Salmon",
price: 14,
calories: 450,
desc: "Atlantic salmon with herbs.",
image: "assets/content/items/restaurants/grilled_salmon.webp",
effects: [
{ stat: "hunger", value: -50, type: "instant" },
{ stat: "energy", value: 15, type: "instant" },
{ stat: "mood", value: 5, type: "instant" }
]
},
{
id: "steak_frites",
name: "Steak & Frites",
price: 15,
calories: 750,
desc: "Grilled ribeye with fries.",
image: "assets/content/items/restaurants/steak_frites.webp",
effects: [
{ stat: "hunger", value: -55, type: "instant" },
{ stat: "energy", value: 20, type: "instant" }
]
},
/* ── RUBY'S DINER NEW ITEMS ── */
{
id: "chili",
name: "Chili",
price: 7,
calories: 300,
desc: "Slow-cooked beef chili with cornbread on the side.",
image: "assets/content/items/restaurants/chili.webp",
effects: [
{ stat: "hunger", value: -30, type: "instant" },
{ stat: "energy", value: 8, type: "instant" },
{ stat: "mood", value: 3, type: "instant" }
]
},
{
id: "blt_sandwich",
name: "BLT Sandwich",
price: 9,
calories: 420,
desc: "Bacon, lettuce, tomato on toasted white bread.",
image: "assets/content/items/restaurants/blt_sandwich.webp",
effects: [
{ stat: "hunger", value: -38, type: "instant" },
{ stat: "energy", value: 10, type: "instant" }
]
},
{
id: "reuben_sandwich",
name: "Reuben Sandwich",
price: 11,
calories: 550,
desc: "Corned beef, sauerkraut, swiss cheese, thousand island on rye.",
image: "assets/content/items/restaurants/reuben_sandwich.webp",
effects: [
{ stat: "hunger", value: -42, type: "instant" },
{ stat: "energy", value: 12, type: "instant" }
]
},
{
id: "fried_chicken_plate",
name: "Fried Chicken Plate",
price: 13,
calories: 650,
desc: "Crispy Southern fried chicken with mashed potatoes and gravy.",
image: "assets/content/items/restaurants/fried_chicken_plate.webp",
effects: [
{ stat: "hunger", value: -52, type: "instant" },
{ stat: "energy", value: 18, type: "instant" }
]
},
{
id: "meatloaf",
name: "Meatloaf",
price: 11,
calories: 500,
desc: "Classic meatloaf with mashed potatoes and green beans.",
image: "assets/content/items/restaurants/meatloaf.webp",
effects: [
{ stat: "hunger", value: -48, type: "instant" },
{ stat: "energy", value: 15, type: "instant" },
{ stat: "mood", value: 5, type: "instant" }
]
},
{
id: "french_toast",
name: "French Toast",
price: 9,
calories: 420,
desc: "Thick-cut brioche, powdered sugar, maple syrup.",
image: "assets/content/items/restaurants/french_toast.webp",
effects: [
{ stat: "hunger", value: -35, type: "instant" },
{ stat: "energy", value: 10, type: "instant" },
{ stat: "mood", value: 4, type: "instant" }
]
},
{
id: "waffles",
name: "Waffles",
price: 9,
calories: 480,
desc: "Golden Belgian waffles with fresh berries and whipped cream.",
image: "assets/content/items/restaurants/waffles.webp",
effects: [
{ stat: "hunger", value: -38, type: "instant" },
{ stat: "energy", value: 10, type: "instant" },
{ stat: "mood", value: 3, type: "instant" }
]
},
{
id: "omelette",
name: "Omelette",
price: 10,
calories: 350,
desc: "Three-egg omelette with cheese, peppers, and onions.",
image: "assets/content/items/restaurants/omelette.webp",
effects: [
{ stat: "hunger", value: -40, type: "instant" },
{ stat: "energy", value: 15, type: "instant" }
]
},
{
id: "apple_pie",
name: "Apple Pie",
price: 6,
calories: 400,
desc: "Warm slice with a scoop of vanilla ice cream.",
image: "assets/content/items/restaurants/apple_pie.webp",
effects: [
{ stat: "hunger", value: -20, type: "instant" },
{ stat: "mood", value: 8, type: "instant" },
{ stat: "stress", value: -5, type: "instant" }
]
},
/* ── FOOD COURT SETS ── */
{
id: "fc_smash_burger_menu",
name: "Smash Burger Menu",
price: 13,
calories: 900,
desc: "Double smash patty, fries, and a fountain drink.",
image: "assets/content/items/restaurants/fc_smash_burger_menu.webp",
effects: [
{ stat: "hunger", value: -60, type: "instant" },
{ stat: "thirst", value: -35, type: "instant" },
{ stat: "energy", value: 20, type: "instant" }
]
},
{
id: "fc_chicken_basket_menu",
name: "Crispy Chicken Menu",
price: 14,
calories: 950,
desc: "Crispy fried chicken tenders, waffle fries, and a drink.",
image: "assets/content/items/restaurants/fc_chicken_basket_menu.webp",
effects: [
{ stat: "hunger", value: -58, type: "instant" },
{ stat: "thirst", value: -35, type: "instant" },
{ stat: "energy", value: 20, type: "instant" }
]
},
{
id: "fc_pizza_menu",
name: "Pizza Menu",
price: 12,
calories: 820,
desc: "Two slices of cheesy pizza, fries, and a drink.",
image: "assets/content/items/restaurants/fc_pizza_menu.webp",
effects: [
{ stat: "hunger", value: -55, type: "instant" },
{ stat: "thirst", value: -35, type: "instant" },
{ stat: "energy", value: 18, type: "instant" },
{ stat: "mood", value: 3, type: "instant" }
]
},
{
id: "fc_burrito_bowl_menu",
name: "Burrito Bowl Menu",
price: 13,
calories: 880,
desc: "Rice, beans, grilled chicken, salsa, and a drink.",
image: "assets/content/items/restaurants/fc_burrito_bowl_menu.webp",
effects: [
{ stat: "hunger", value: -58, type: "instant" },
{ stat: "thirst", value: -38, type: "instant" },
{ stat: "energy", value: 18, type: "instant" }
]
},
{
id: "fc_hot_dog_menu",
name: "Hot Dog Menu",
price: 11,
calories: 720,
desc: "Loaded hot dog with mustard and onions, fries, and a drink.",
image: "assets/content/items/restaurants/fc_hot_dog_menu.webp",
effects: [
{ stat: "hunger", value: -50, type: "instant" },
{ stat: "thirst", value: -35, type: "instant" },
{ stat: "energy", value: 15, type: "instant" }
]
},
{
id: "fc_veggie_wrap_menu",
name: "Veggie Wrap Menu",
price: 12,
calories: 680,
desc: "Grilled veggies and hummus in a flatbread, side salad, and a drink.",
image: "assets/content/items/restaurants/fc_veggie_wrap_menu.webp",
effects: [
{ stat: "hunger", value: -48, type: "instant" },
{ stat: "thirst", value: -35, type: "instant" },
{ stat: "energy", value: 15, type: "instant" },
{ stat: "mood", value: 4, type: "instant" }
]
}
],
drinks: [
{
id: "water",
name: "Water",
price: 0,
calories: 0,
desc: "Still or sparkling. Complimentary.",
image: "assets/content/items/restaurants/water.webp",
effects: [
{ stat: "thirst", value: -40, type: "instant" }
]
},
{
id: "coffee",
name: "Coffee",
price: 2.5,
calories: 5,
desc: "Freshly brewed.",
image: "assets/content/items/restaurants/coffee.webp",
effects: [
{ stat: "energy", value: 25, type: "instant" }
]
},
{
id: "dg_americano",
name: "Americano",
price: 3.5,
calories: 10,
desc: "Bold espresso diluted with hot water. Clean and strong.",
image: "assets/content/items/restaurants/coffee.webp",
effects: [
{ stat: "energy", value: 22, type: "instant" },
{ stat: "focus", value: 8, type: "instant" }
]
},
{
id: "dg_latte",
name: "Latte",
price: 4.5,
calories: 140,
desc: "Smooth milk-forward coffee with a mellow finish.",
image: "assets/content/items/restaurants/coffee.webp",
effects: [
{ stat: "energy", value: 16, type: "instant" },
{ stat: "stress", value: -6, type: "instant" },
{ stat: "thirst", value: -8, type: "instant" }
]
},
{
id: "dg_cappuccino",
name: "Cappuccino",
price: 4.5,
calories: 110,
desc: "Espresso, steamed milk, and dense foam on top.",
image: "assets/content/items/restaurants/coffee.webp",
effects: [
{ stat: "energy", value: 20, type: "instant" },
{ stat: "mood", value: 4, type: "instant" }
]
},
{
id: "dg_mocha",
name: "Mocha",
price: 5,
calories: 190,
desc: "Espresso with chocolate and steamed milk.",
image: "assets/content/items/restaurants/coffee.webp",
effects: [
{ stat: "energy", value: 18, type: "instant" },
{ stat: "mood", value: 8, type: "instant" },
{ stat: "stress", value: -4, type: "instant" }
]
},
{
id: "dg_coldbrew",
name: "Cold Brew",
price: 4.5,
calories: 8,
desc: "Slow-steeped coffee served cold over ice.",
image: "assets/content/items/restaurants/coffee.webp",
effects: [
{ stat: "energy", value: 28, type: "instant" },
{ stat: "focus", value: 10, type: "instant" },
{ stat: "thirst", value: -12, type: "instant" }
]
},
{
id: "iced_tea",
name: "Iced Tea",
price: 3.5,
calories: 80,
desc: "Refreshing and cold.",
image: "assets/content/items/restaurants/iced_tea.webp",
effects: [
{ stat: "thirst", value: -35, type: "instant" },
{ stat: "energy", value: 5, type: "instant" }
]
},
{
id: "soda",
name: "Soda",
price: 2.5,
calories: 100,
desc: "Cola, lemon-lime, or root beer.",
image: "assets/content/items/restaurants/soda.webp",
effects: [
{ stat: "thirst", value: -25, type: "instant" }
]
},
{
id: "fresh_juice",
name: "Fresh Juice",
price: 5,
calories: 120,
desc: "Seasonal fruit juice.",
image: "assets/content/items/restaurants/fresh_juice.webp",
effects: [
{ stat: "thirst", value: -30, type: "instant" },
{ stat: "energy", value: 8, type: "instant" }
]
},
{
id: "milkshake",
name: "Milkshake",
price: 6,
calories: 450,
desc: "Thick shake vanilla, chocolate, or strawberry.",
image: "assets/content/items/restaurants/milkshake.webp",
effects: [
{ stat: "thirst", value: -40, type: "instant" },
{ stat: "mood", value: 5, type: "instant" }
]
},
{
id: "lemonade",
name: "Lemonade",
price: 3,
calories: 100,
desc: "Fresh-squeezed, sweetened just right.",
image: "assets/content/items/restaurants/lemonade.webp",
effects: [
{ stat: "thirst", value: -35, type: "instant" },
{ stat: "mood", value: 3, type: "instant" }
]
},
{
id: "hot_chocolate",
name: "Hot Chocolate",
price: 3.5,
calories: 180,
desc: "Rich and creamy, topped with mini marshmallows.",
image: "assets/content/items/restaurants/hot_chocolate.webp",
effects: [
{ stat: "thirst", value: -20, type: "instant" },
{ stat: "mood", value: 6, type: "instant" },
{ stat: "stress", value: -5, type: "instant" }
]
},
/* Food court: same stats as diner drinks, takeaway cup art only */
{
id: "fc_soda_cup",
name: "Soda",
price: 2.5,
calories: 100,
desc: "Fountain soda in a to-go cup.",
image: "assets/content/items/restaurants/fc_soda_cup.webp",
effects: [
{ stat: "thirst", value: -25, type: "instant" }
]
},
{
id: "fc_coffee_cup",
name: "Coffee",
price: 2.5,
calories: 5,
desc: "Hot coffee in a to-go cup.",
image: "assets/content/items/restaurants/fc_coffee_cup.webp",
effects: [
{ stat: "energy", value: 25, type: "instant" }
]
},
{
id: "fc_iced_tea_cup",
name: "Iced Tea",
price: 3.5,
calories: 80,
desc: "Iced tea in a to-go cup.",
image: "assets/content/items/restaurants/fc_iced_tea_cup.webp",
effects: [
{ stat: "thirst", value: -35, type: "instant" },
{ stat: "energy", value: 5, type: "instant" }
]
},
{
id: "fc_milkshake_cup",
name: "Milkshake",
price: 6,
calories: 450,
desc: "Thick shake in a to-go cup.",
image: "assets/content/items/restaurants/fc_milkshake_cup.webp",
effects: [
{ stat: "thirst", value: -40, type: "instant" },
{ stat: "mood", value: 5, type: "instant" }
]
}
]
};
setup.restaurantMenus = {
dinerRubys: {
name: "Ruby's Diner",
foods: [
"house_salad", "soup_of_day", "chili",
"diner_burger", "blt_sandwich", "reuben_sandwich", "grilled_cheese", "club_sandwich",
"fried_chicken_plate", "meatloaf",
"mac_cheese", "pasta_carbonara", "grilled_salmon", "steak_frites",
"pancakes", "french_toast", "waffles", "eggs_bacon", "omelette",
"apple_pie"
],
drinks: ["water", "coffee", "iced_tea", "soda", "lemonade", "fresh_juice", "hot_chocolate", "milkshake"],
back: "dinerRubys"
},
foodCourtMall: {
name: "Food Court",
foods: ["fc_smash_burger_menu", "fc_chicken_basket_menu", "fc_pizza_menu", "fc_burrito_bowl_menu", "fc_hot_dog_menu", "fc_veggie_wrap_menu"],
drinks: ["water", "fc_soda_cup", "fc_coffee_cup", "fc_iced_tea_cup", "fc_milkshake_cup"],
back: "floorThird"
},
dailyGrind: {
name: "The Daily Grind",
foods: [],
drinks: ["dg_americano", "dg_latte", "dg_cappuccino", "dg_mocha", "dg_coldbrew"],
back: "shopCoffeeOldtown"
}
};
<</script>>
<<nobr>>
<<set _menuId = $restaurantMenuId>>
<<if ndef _menuId>><<set _menuId = "dinerRubys">><</if>>
<<set _menu = setup.restaurantMenus[_menuId]>>
<<if _menu && _menu.back>><<set $location = _menu.back>><</if>>
<</nobr>>
<<restaurant _menuId>><<nobr>>
/* ==========================================
RESTAURANT WIDGET
Link to restaurant menu. Back passage comes from database (menu.back) or $location.
Usage: <<restaurantLink "dinerRubys">>
Or: <<restaurantLink "dinerRubys" "Order food">> → menuId, label
========================================== */
<<widget "restaurantLink">><<nobr>>
<<set _menuId = $args[0]>>
<<set _label = $args[1] || "Order">>
<<set $._restaurantMenuIdNext = _menuId>>
<<btn _label "RestaurantMenu">><<set $restaurantMenuId = $._restaurantMenuIdNext>><</btn>>
<</nobr>><</widget>>
<</nobr>><div class="divnobr">
<<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<</if>>
<<if !$_navigatingBackward>>
<<silently>>
/* Work day number: stable across all shifts in the same day.
Before 1st shift: totalDaysWorked=2, workedToday=false → 2+1=3
Before 2nd shift: totalDaysWorked=3, workedToday=true → 3+0=3 */
<<set _workDay = ($jobState.totalDaysWorked || 0) + ($jobState.workedToday ? 0 : 1)>>
<<if $job && $job.id === "ruby_dishwasher" && _workDay === 3 && !$flags.vinceInspectionDay3Shown>>
<<if !$flags.vinceDay3InspectShift>><<set $flags.vinceDay3InspectShift = random(1, 2)>><</if>>
<<set _vinceNextShiftNum = ($jobState.shiftsToday || 0) + 1>>
<<if _vinceNextShiftNum === $flags.vinceDay3InspectShift>>
<<set $flags.vinceInspectionDay3Shown = true>>
<<goto "dinerWork_event_vinceInspection_day3">>
<</if>>
<</if>>
<</silently>>
<<jobExecuteShift $selectedDuration>>
<<if !$jobShiftSuccess>>
<<goto "jobWorkSelect">>
<<else>>
<<set $location = "dinerRubys">>
<<set $activityOrigin = "dinerRubys">>
<<updateCharacterLocations>>
<<silently>>
<<set _energyTier = ($energy > 60) ? "high" : ($energy > 30) ? "mid" : "low">>
<<set _h = $timeSys.hour>>
<<set _timeSlot = ((_h >= 12 && _h <= 14) || (_h >= 17 && _h <= 20)) ? "rush" : "quiet">>
<<set _n = random(1, 2)>>
<<set _key = _energyTier + "_" + _timeSlot + "_" + _n>>
<</silently>>
<<switch _key>>
<<case "high_rush_1">>
<<narrative "Shift">>
The lunch rush hit hard. Plates stacked up fast, you cleared them faster. Hands moving before your brain caught up. The station stayed clean the whole way through.
<</narrative>>
<<case "high_rush_2">>
<<narrative "Shift">>
Dinner crowd kept the plates coming. You found a rhythm early and didn't lose it. Steam, soap, stack, repeat. By the time it slowed you were still ahead.
<</narrative>>
<<case "high_quiet_1">>
<<narrative "Shift">>
Slow shift. You took your time. Got into the corners of the pans, rinsed everything twice. The station is cleaner than it needs to be. You're okay with that.
<</narrative>>
<<case "high_quiet_2">>
<<narrative "Shift">>
Not much came through. You stayed busy anyway reorganized the rack, restacked the trays. Shift done, everything in its place.
<</narrative>>
<<case "mid_rush_1">>
<<narrative "Shift">>
Rush came in waves. You kept up but not easily. By the last batch your arms were going through the motions. Plates clean. That's what matters.
<</narrative>>
<<case "mid_rush_2">>
<<narrative "Shift">>
Kitchen didn't stop. Neither did you. Head down, work through it. At some point you stopped thinking and just moved. Shift's done. You made it.
<</narrative>>
<<case "mid_quiet_1">>
<<narrative "Shift">>
Steady pace. Nothing dramatic. You washed, stacked, wiped. Somewhere in the middle your mind went somewhere else. Station's clear. You're done.
<</narrative>>
<<case "mid_quiet_2">>
<<narrative "Shift">>
Slow but long. The kind of shift where time moves differently. You kept working. Your hands knew what to do even when you weren't paying attention.
<</narrative>>
<<case "low_rush_1">>
<<narrative "Shift">>
The rush didn't care that you were running low. You didn't stop you just slowed down. Arms heavy. Back tight. You got through it anyway.
<</narrative>>
<<case "low_rush_2">>
<<narrative "Shift">>
Hard push through the dinner crowd. Your body had less to give than the shift asked for. You gave it anyway. Station's clear. You feel it in your shoulders.
<</narrative>>
<<case "low_quiet_1">>
<<narrative "Shift">>
Slow shift but your body didn't feel it that way. You kept going on something close to habit. At some point you leaned on the edge of the sink and stayed there longer than you needed to. Your back keeps reminding you the storage stool exists.
<</narrative>>
<<case "low_quiet_2">>
<<narrative "Shift">>
Not much came through, which helped. You moved at whatever pace your body allowed. The last rack came out and you just stood there a second before you turned it off. Done.
<</narrative>>
<<default>>
<<narrative "Shift">>
Another shift at the sink. Hands raw, stacks cleared. Time to head out front.
<</narrative>>
<</switch>>
<<set _vidNum = random(1, 3)>>
<<set _dishVid = "assets/content/scenes/oldtown/rubysDiner/work/dishwash/vids/dinerDishwash" + _vidNum + ".mp4">>
<<vid _dishVid "100%">>
/* Mini event check */
<<if ndef $daily.dishwashRushSurgeToday>><<set $daily.dishwashRushSurgeToday = false>><</if>>
<<if ndef $daily.dishwashVinceCheckToday>><<set $daily.dishwashVinceCheckToday = false>><</if>>
<<silently>>
<<set _dishSkill = ($skills && $skills.practical && $skills.practical.dishwashing) ? $skills.practical.dishwashing : 0>>
<<set _accidentChance = (_dishSkill <= 10) ? 40 : (_dishSkill <= 25) ? 20 : 5>>
<<set _accidentRoll = random(1, 100)>>
<<set _eventFired = false>>
<<set _eventType = "">>
<<if _accidentRoll <= _accidentChance>>
<<set _negPool = ["broken", "clog"]>>
<<if _dishSkill <= 15>>
<<run _negPool.push("burn")>>
<</if>>
<<set _eventType = _negPool[Math.floor(Math.random() * _negPool.length)]>>
<<set _eventFired = true>>
<<else>>
<<set _neutralRoll = random(1, 100)>>
<<if _neutralRoll <= 15 && !$daily.dishwashRushSurgeToday>>
<<set _eventType = "rushSurge">>
<<set _eventFired = true>>
<<set $daily.dishwashRushSurgeToday = true>>
<<elseif _neutralRoll <= 25 && !$daily.dishwashVinceCheckToday>>
<<set _eventType = "vinceCheck">>
<<set _eventFired = true>>
<<set $daily.dishwashVinceCheckToday = true>>
<<else>>
<<set _positivePool = []>>
<<if $characters.dinerWaitress1.stats.friendship >= 35 && $energy <= 35>>
<<run _positivePool.push("emmaWater")>>
<</if>>
<<if $characters.dinerChef.stats.friendship >= 35 && $energy <= 35>>
<<run _positivePool.push("jamesSnack")>>
<</if>>
<<if $characters.dinerWaitress3.stats.friendship >= 25>>
<<run _positivePool.push("sofiaChat")>>
<</if>>
<<if $characters.dinerDishwasher.stats.friendship >= 30 && ((_h >= 12 && _h <= 14) || (_h >= 17 && _h <= 20))>>
<<run _positivePool.push("mikeHelp")>>
<</if>>
<<if $characters.dinerClerk.stats.friendship >= 30 && !(_h >= 12 && _h <= 14) && !(_h >= 17 && _h <= 20)>>
<<run _positivePool.push("tomChat")>>
<</if>>
<<if _positivePool.length > 0>>
<<set _eventType = _positivePool[Math.floor(Math.random() * _positivePool.length)]>>
<<set _eventFired = true>>
<</if>>
<</if>>
<</if>>
<</silently>>
<<if _eventFired && _eventType === "rushSurge">>
<<goto "dinerWork_event_rushSurge">>
<<else>>
<<if _eventFired>>
<<if _eventType === "broken">>
<<include "dinerWork_event_broken">>
<<elseif _eventType === "clog">>
<<include "dinerWork_event_clog">>
<<elseif _eventType === "burn">>
<<include "dinerWork_event_burn">>
<<elseif _eventType === "emmaWater">>
<<include "dinerWork_event_emmaWater">>
<<elseif _eventType === "jamesSnack">>
<<include "dinerWork_event_jamesSnack">>
<<elseif _eventType === "sofiaChat">>
<<include "dinerWork_event_sofiaChat">>
<<elseif _eventType === "mikeHelp">>
<<include "dinerWork_event_mikeHelp">>
<<elseif _eventType === "tomChat">>
<<include "dinerWork_event_tomChat">>
<<elseif _eventType === "vinceCheck">>
<<include "dinerWork_event_vinceCheck">>
<</if>>
<</if>>
<<include "RubysDishwashWork_afterMini">>
<</if>>
<</if>>
<</if>>
</div>/* ==========================================
Dishwasher shift outro: Diana gate + Finish shift.
Diana arrives 2+ days after "Unfinished Thoughts" quest completion.
Included from RubysDishwashWork and rush sub-passages.
Diana: set dianaEventShown then <<btn>> (no <<goto>>) so history keeps the flag and Back works.
========================================== */
<<silently>>
<<set _dianaJob = ($job && $job.id) ? setup.jobs[$job.id] : null>>
<<set _dianaReqDay = (_dianaJob && _dianaJob.requiredHoursPerDay != null) ? _dianaJob.requiredHoursPerDay : 8>>
<<set _jakeFriendship = ($characters && $characters.dinerWaitress3 && $characters.dinerWaitress3.stats) ? ($characters.dinerWaitress3.stats.friendship || 0) : 0>>
/* Diana gate: quest "Unfinished Thoughts" must be done + 2 days elapsed */
<<set _vinceQuestDoneDate = ($questState && $questState.completedDates && $questState.completedDates.vince_day3_family) || 0>>
<<set _today = ($timeSys.year || 0) * 10000 + ($timeSys.month || 0) * 100 + ($timeSys.day || 0)>>
/* completedDates stores YYYYMMDD numeric; add 2 days using JS Date for month rollover safety */
<<set _doneY = Math.floor(_vinceQuestDoneDate / 10000)>>
<<set _doneM = Math.floor((_vinceQuestDoneDate % 10000) / 100)>>
<<set _doneD = _vinceQuestDoneDate % 100>>
<<set _targetDateObj = (_vinceQuestDoneDate > 0) ? new Date(_doneY, _doneM - 1, _doneD + 2) : null>>
<<set _targetDate = _targetDateObj ? (_targetDateObj.getFullYear() * 10000 + (_targetDateObj.getMonth() + 1) * 100 + _targetDateObj.getDate()) : 99999999>>
<</silently>>
<<if $job && $job.id === "ruby_dishwasher" && _vinceQuestDoneDate > 0 && _today >= _targetDate && (parseFloat($jobState.hoursToday) || 0) >= _dianaReqDay && !$flags.dianaEventShown>>
<<set $flags.dianaEventShown = true>>
<div class="location-actions">
<<btn "See what's going on up front" "dinerWork_event_dianaArrival">><</btn>>
</div>
<<elseif $job && $job.id === "ruby_dishwasher" && ($jobState.totalDaysWorked || 0) >= 11 && (parseFloat($jobState.hoursToday) || 0) >= _dianaReqDay && _jakeFriendship >= 20 && !$flags.jakeCoffeeInviteWeek3Shown>>
<<set $flags.jakeCoffeeInviteWeek3Shown = true>>
<div class="location-actions">
<<btn "Jake catches you before you leave" "dinerWork_event_jakeCoffeeInvite">><</btn>>
</div>
<<else>>
<div class="location-actions">
<<btn "Finish shift" "dinerRubys">>
<<silently>>
/* Normal shift outro (not first-day tutorial passage): set walk-home gate for Maplewood same flag as dinerFirstdayJob_dishwasher */
<<if $job && $job.id === "ruby_dishwasher" && ($jobState.totalDaysWorked || 0) === 1 && !$flags.firstWorkDayWalkHome>>
<<set $flags.firstWorkDayWalkEvent = true>>
<</if>>
<</silently>>
<</btn>>
</div>
<</if>>
<<silently>>
<<if $saveVersion lt 1>>
/* Job system migration: trust-based work state (backward compatibility). */
<<if ndef $job>><<set $job = { id: null, tier: 1, startedOn: null }>><</if>>
<<if ndef $jobState>><<set $jobState = {}>><</if>>
<<if ndef $jobState.hoursThisWeek>><<set $jobState.hoursThisWeek = 0>><</if>>
<<if ndef $jobState.hoursToday>><<set $jobState.hoursToday = 0>><</if>>
<<if ndef $jobState.shiftsToday>><<set $jobState.shiftsToday = 0>><</if>>
<<if ndef $jobState.totalDaysWorked>><<set $jobState.totalDaysWorked = 0>><</if>>
<<if ndef $jobState.jobExperience>><<set $jobState.jobExperience = 0>><</if>>
<<if ndef $jobState.weeklyEarnings>><<set $jobState.weeklyEarnings = 0>><</if>>
<<if ndef $jobState.weeklyDeductions>><<set $jobState.weeklyDeductions = 0>><</if>>
<<if ndef $jobState.lastPayWeek>><<set $jobState.lastPayWeek = null>><</if>>
<<if ndef $jobState.lastShiftEndHour>><<set $jobState.lastShiftEndHour = null>><</if>>
<<if ndef $jobState.minHoursWarningCount>><<set $jobState.minHoursWarningCount = 0>><</if>>
<<if ndef $jobState.firstWorkDayEventShown>><<set $jobState.firstWorkDayEventShown = false>><</if>>
<<if ndef $jobState.insufficientDaysThisWeek>><<set $jobState.insufficientDaysThisWeek = 0>><</if>>
<<if ndef $jobState.bossWantsToSeePlayer>><<set $jobState.bossWantsToSeePlayer = false>><</if>>
<<if ndef $jobState.visitedBossThisMonday>><<set $jobState.visitedBossThisMonday = false>><</if>>
<<if ndef $jobState.freeMealUsedToday>><<set $jobState.freeMealUsedToday = false>><</if>>
<<if ndef $jobState.workedToday>><<set $jobState.workedToday = false>><</if>>
<<if ndef $jobState.workTrustScore>><<set $jobState.workTrustScore = 60>><</if>>
<<if ndef $jobState.workTrustBand>><<set $jobState.workTrustBand = "mid">><</if>>
<<if ndef $jobState.bossMeetingRequired>><<set $jobState.bossMeetingRequired = false>><</if>>
<<if ndef $jobState.bossMeetingReason>><<set $jobState.bossMeetingReason = "">><</if>>
<<if ndef $jobState.terminationPending>><<set $jobState.terminationPending = false>><</if>>
<<if ndef $jobState.promotionPending>><<set $jobState.promotionPending = false>><</if>>
<<if ndef $jobState.pendingTier>><<set $jobState.pendingTier = 0>><</if>>
<<if ndef $jobState.weeklyPayPending>><<set $jobState.weeklyPayPending = null>><</if>>
<<if ndef $jobState.lastInsufficientType>><<set $jobState.lastInsufficientType = "">><</if>>
<<if ndef $jobState.lastInsufficientHours>><<set $jobState.lastInsufficientHours = 0>><</if>>
<<if ndef $jobState.excuseForDateKey>><<set $jobState.excuseForDateKey = 0>><</if>>
<<if ndef $jobState.excuseMode>><<set $jobState.excuseMode = "">><</if>>
<<if ndef $jobState.excuseSentToday>><<set $jobState.excuseSentToday = false>><</if>>
<<if ndef $jobState.bossLieAttemptedThisMeeting>><<set $jobState.bossLieAttemptedThisMeeting = false>><</if>>
<<if ndef $jobState.bossLieLastResult>><<set $jobState.bossLieLastResult = "">><</if>>
<<if ndef $jobState.bossLieLastReason>><<set $jobState.bossLieLastReason = "">><</if>>
<<if ndef $jobState.bossLieLastKey>><<set $jobState.bossLieLastKey = "">><</if>>
<<if ndef $jobState.bossLieLastChance>><<set $jobState.bossLieLastChance = 0>><</if>>
<<if ndef $jobState.bossLieLastPenaltyAmount>><<set $jobState.bossLieLastPenaltyAmount = 0>><</if>>
/* Ensure trust band matches migrated score. */
<<if ($jobState.workTrustScore || 0) >= 70>>
<<set $jobState.workTrustBand = "high">>
<<elseif ($jobState.workTrustScore || 0) >= 30>>
<<set $jobState.workTrustBand = "mid">>
<<else>>
<<set $jobState.workTrustBand = "low">>
<</if>>
/* Gym Mini-Game: daily workout flags */
<<if ndef $daily>><<set $daily = {}>><</if>>
<<if ndef $daily.gymUpperDone>><<set $daily.gymUpperDone = false>><</if>>
<<if ndef $daily.gymCoreDone>><<set $daily.gymCoreDone = false>><</if>>
<<if ndef $daily.gymLowerDone>><<set $daily.gymLowerDone = false>><</if>>
<<if ndef $daily.gymCardioDone>><<set $daily.gymCardioDone = false>><</if>>
/* Gym Mini-Game: settings toggle */
<<if ndef $gameSettings>><<set $gameSettings = {}>><</if>>
<<if ndef $gameSettings.gymMiniGameEnabled>><<set $gameSettings.gymMiniGameEnabled = true>><</if>>
/* Gym Mini-Game: tutorial flag */
<<if ndef $flags>><<set $flags = {}>><</if>>
<<if ndef $flags.gymMiniGameTutorialSeen>><<set $flags.gymMiniGameTutorialSeen = false>><</if>>
<<if ndef $flags.bodyAwarenessTier1>><<set $flags.bodyAwarenessTier1 = false>><</if>>
<<if ndef $flags.bodyAwarenessTier2>><<set $flags.bodyAwarenessTier2 = false>><</if>>
<<if ndef $flags.bodyAwarenessTier3>><<set $flags.bodyAwarenessTier3 = false>><</if>>
<<if ndef $flags.brotherPornFirstSeen>><<set $flags.brotherPornFirstSeen = false>><</if>>
<<if ndef $flags.brotherPornSecondPeek>><<set $flags.brotherPornSecondPeek = false>><</if>>
<<if ndef $flags.brotherPornLookButtonUnlocked>><<set $flags.brotherPornLookButtonUnlocked = false>><</if>>
<<if ndef $flags.brotherPornWatchFirstDone>><<set $flags.brotherPornWatchFirstDone = false>><</if>>
<<if ndef $flags.pornWatchFirstDone>><<set $flags.pornWatchFirstDone = $flags.brotherPornWatchFirstDone || false>><</if>>
<<if ndef $flags.privateContentHabit>><<set $flags.privateContentHabit = false>><</if>>
<<if ndef $flags.firstCantSleepStoryDone>><<set $flags.firstCantSleepStoryDone = false>><</if>>
<<if ndef $flags.masturbationUnlocked>><<set $flags.masturbationUnlocked = false>><</if>>
<<if ndef $flags.nightRestlessUnlocked>><<set $flags.nightRestlessUnlocked = false>><</if>>
<<if ndef $flags.jakeCoffeeInviteWeek3Shown>><<set $flags.jakeCoffeeInviteWeek3Shown = false>><</if>>
/* Gym Membership: 30-day access for old saves. */
<<if ndef $gymMembershipDaysRemaining>><<set $gymMembershipDaysRemaining = $flags.gymMembershipActive ? 30 : 0>><</if>>
<<if ndef $gymMembershipStartedAt>><<set $gymMembershipStartedAt = null>><</if>>
<<if ndef $afterShowerMirrorLastDayKey>><<set $afterShowerMirrorLastDayKey = 0>><</if>>
<<if ndef $brotherPornSecondLastSeenDayKey>><<set $brotherPornSecondLastSeenDayKey = 0>><</if>>
<<if ndef $brotherPornPhase2Step>><<set $brotherPornPhase2Step = 0>><</if>>
<<if ndef $streetAttentionLastMinuteStamp>><<set $streetAttentionLastMinuteStamp = 0>><</if>>
<<if ndef $streetAttentionLastVariant>><<set $streetAttentionLastVariant = "">><</if>>
<<if ndef $streetAttentionVariantLastMinute>><<set $streetAttentionVariantLastMinute = {}>><</if>>
<<if ndef $nightRestlessEventDayKey>><<set $nightRestlessEventDayKey = 0>><</if>>
<<if ndef $nightRestlessEventSeenCount>><<set $nightRestlessEventSeenCount = 0>><</if>>
<<if ndef $nightRestlessSource>><<set $nightRestlessSource = "">><</if>>
<<if ndef $gameSettings>><<set $gameSettings = {}>><</if>>
<<if ndef $gameSettings.arousalAmbient>><<set $gameSettings.arousalAmbient = true>><</if>>
<<set _pornDefaults = { handjob: 0, blowjob: 0, vanilla: 0, anal: 0, lesbian: 0, bbc: 0, creampie: 0, mff: 0, mmf: 0, gangbang: 0, incest: 0 }>>
<<set _pornFirstSeenDefaults = { handjob: false, blowjob: false, vanilla: false, anal: false, lesbian: false, bbc: false, creampie: false, mff: false, mmf: false, gangbang: false, incest: false }>>
<<if ndef $porn>>
<<set $porn = { categoriesUnlocked: (def $pornCategoriesUnlocked ? $pornCategoriesUnlocked : ["handjob"]), categoryWatchCounts: (def $pornCategoryWatchCounts ? $pornCategoryWatchCounts : clone(_pornDefaults)), brotherRoomCategoryWatchCounts: (def $pornBrotherRoomCategoryWatchCounts ? $pornBrotherRoomCategoryWatchCounts : clone(_pornDefaults)), brotherRoomCategoryDailyWatchCounts: (def $pornBrotherRoomCategoryDailyWatchCounts ? $pornBrotherRoomCategoryDailyWatchCounts : clone(_pornDefaults)), brotherRoomCategoryDailyDayKey: (def $pornBrotherRoomCategoryDailyDayKey ? $pornBrotherRoomCategoryDailyDayKey : 0), brotherRoomCategoriesCountedToday: (def $pornBrotherRoomCategoriesCountedToday ? $pornBrotherRoomCategoriesCountedToday : []), categoryProgressCounts: (def $pornCategoryProgressCounts ? $pornCategoryProgressCounts : clone(_pornDefaults)), categoryProgressDayKey: (def $pornCategoryProgressDayKey ? $pornCategoryProgressDayKey : 0), categoriesProgressedToday: (def $pornCategoriesProgressedToday ? $pornCategoriesProgressedToday : []), categoryTargets: (def $pornCategoryTargets ? $pornCategoryTargets : { handjob: random(3, 5) }), pendingUnlock: (def $pornPendingUnlock ? $pornPendingUnlock : ""), selectedCategory: (def $pornSelectedCategory ? $pornSelectedCategory : ""), returnPassage: (def $pornReturnPassage ? $pornReturnPassage : "brotherComputerMenu"), source: (def $pornSource ? $pornSource : "brotherRoom"), arousalDayKey: (def $pornArousalDayKey ? $pornArousalDayKey : 0), watchCountToday: (def $pornWatchCountToday ? $pornWatchCountToday : 0), maxDailyWatches: 6, skillGainDayKey: (def $pornSkillGainDayKey ? $pornSkillGainDayKey : 0), skillsGainedToday: (def $pornSkillsGainedToday ? $pornSkillsGainedToday : []), categoryFirstWatchSeen: (def $pornCategoryFirstWatchSeen ? $pornCategoryFirstWatchSeen : clone(_pornFirstSeenDefaults)) }>>
<</if>>
<<if ndef $porn.categoriesUnlocked>><<set $porn.categoriesUnlocked = ["handjob"]>><</if>>
<<if ndef $porn.categoryWatchCounts>><<set $porn.categoryWatchCounts = clone(_pornDefaults)>><</if>>
<<if ndef $porn.brotherRoomCategoryWatchCounts>><<set $porn.brotherRoomCategoryWatchCounts = clone(_pornDefaults)>><</if>>
<<if ndef $porn.brotherRoomCategoryDailyWatchCounts>><<set $porn.brotherRoomCategoryDailyWatchCounts = clone(_pornDefaults)>><</if>>
<<if ndef $porn.brotherRoomCategoryDailyDayKey>><<set $porn.brotherRoomCategoryDailyDayKey = 0>><</if>>
<<if ndef $porn.brotherRoomCategoriesCountedToday>><<set $porn.brotherRoomCategoriesCountedToday = []>><</if>>
<<if ndef $porn.categoryProgressCounts>><<set $porn.categoryProgressCounts = clone(_pornDefaults)>><</if>>
<<if ndef $porn.categoryProgressDayKey>><<set $porn.categoryProgressDayKey = 0>><</if>>
<<if ndef $porn.categoriesProgressedToday>><<set $porn.categoriesProgressedToday = []>><</if>>
<<if ndef $porn.categoryTargets>><<set $porn.categoryTargets = { handjob: random(3, 5) }>><</if>>
<<if ndef $porn.categoryTargets.handjob>><<set $porn.categoryTargets.handjob = random(3, 5)>><</if>>
<<if ndef $porn.pendingUnlock>><<set $porn.pendingUnlock = "">><</if>>
<<if ndef $porn.selectedCategory>><<set $porn.selectedCategory = "">><</if>>
<<if ndef $porn.returnPassage or $porn.returnPassage === "">><<set $porn.returnPassage = "brotherComputerMenu">><</if>>
<<if ndef $porn.source or $porn.source === "">><<set $porn.source = "brotherRoom">><</if>>
<<if ndef $porn.arousalDayKey>><<set $porn.arousalDayKey = 0>><</if>>
<<if ndef $porn.watchCountToday>><<set $porn.watchCountToday = 0>><</if>>
<<if ndef $porn.maxDailyWatches>><<set $porn.maxDailyWatches = 6>><</if>>
<<if ndef $porn.skillGainDayKey>><<set $porn.skillGainDayKey = 0>><</if>>
<<if ndef $porn.skillsGainedToday>><<set $porn.skillsGainedToday = []>><</if>>
<<if ndef $porn.categoryFirstWatchSeen>><<set $porn.categoryFirstWatchSeen = clone(_pornFirstSeenDefaults)>><</if>>
<<if ndef $porn.handjobFirstScenePlayed>>
<<set _hjSeen = !!$porn.categoryFirstWatchSeen.handjob>>
<<set _hjCnt = $porn.categoryWatchCounts.handjob || 0>>
<<set $porn.handjobFirstScenePlayed = _hjSeen || (_hjCnt > 0)>>
<</if>>
<<if ndef $porn.blowjobFirstScenePlayed>>
<<set _bjSeen = !!$porn.categoryFirstWatchSeen.blowjob>>
<<set _bjCnt = $porn.categoryWatchCounts.blowjob || 0>>
<<set $porn.blowjobFirstScenePlayed = _bjSeen || (_bjCnt > 0)>>
<</if>>
<<if ndef $porn.repeatVidSrc>><<set $porn.repeatVidSrc = "">><</if>>
<<if ndef $porn.repeatVidWidth>><<set $porn.repeatVidWidth = "100%">><</if>>
<<set _pornAllowedCategories = ["handjob", "blowjob"]>>
<<run $porn.categoriesUnlocked = ($porn.categoriesUnlocked || []).filter(function(_c){ return _pornAllowedCategories.includes(_c); })>>
<<if !$porn.categoriesUnlocked.includes("handjob")>><<run $porn.categoriesUnlocked.unshift("handjob")>><</if>>
<<if $porn.selectedCategory && !_pornAllowedCategories.includes($porn.selectedCategory)>><<set $porn.selectedCategory = "handjob">><</if>>
<<if $porn.pendingUnlock && !_pornAllowedCategories.includes($porn.pendingUnlock)>><<set $porn.pendingUnlock = "">><</if>>
<<for _cat range Object.keys(_pornDefaults)>>
<<if ndef $porn.categoryWatchCounts[_cat]>><<set $porn.categoryWatchCounts[_cat] = 0>><</if>>
<<if ndef $porn.brotherRoomCategoryWatchCounts[_cat]>><<set $porn.brotherRoomCategoryWatchCounts[_cat] = 0>><</if>>
<<if ndef $porn.brotherRoomCategoryDailyWatchCounts[_cat]>><<set $porn.brotherRoomCategoryDailyWatchCounts[_cat] = 0>><</if>>
<<if ndef $porn.categoryProgressCounts[_cat]>><<set $porn.categoryProgressCounts[_cat] = $porn.categoryWatchCounts[_cat] || 0>><</if>>
<<if ndef $porn.categoryFirstWatchSeen[_cat]>><<set $porn.categoryFirstWatchSeen[_cat] = false>><</if>>
<</for>>
<<set _oldDpWatch = $porn.categoryWatchCounts.dp || 0>>
<<set _oldSpitroastWatch = $porn.categoryWatchCounts.spitroast || 0>>
<<set _oldDpProgress = $porn.categoryProgressCounts.dp || 0>>
<<set _oldSpitroastProgress = $porn.categoryProgressCounts.spitroast || 0>>
<<if _oldDpWatch gt 0 || _oldSpitroastWatch gt 0>>
<<set $porn.categoryWatchCounts.mmf = Math.max($porn.categoryWatchCounts.mmf || 0, _oldDpWatch, _oldSpitroastWatch)>>
<</if>>
<<if _oldDpProgress gt 0 || _oldSpitroastProgress gt 0>>
<<set $porn.categoryProgressCounts.mmf = Math.max($porn.categoryProgressCounts.mmf || 0, _oldDpProgress, _oldSpitroastProgress)>>
<</if>>
<<if $porn.categoryFirstWatchSeen.dp || $porn.categoryFirstWatchSeen.spitroast>>
<<set $porn.categoryFirstWatchSeen.mmf = true>>
<</if>>
<<if $porn.categoryTargets.dp || $porn.categoryTargets.spitroast>>
<<set $porn.categoryTargets.mmf = $porn.categoryTargets.mmf || Math.max($porn.categoryTargets.dp || 0, $porn.categoryTargets.spitroast || 0)>>
<</if>>
<<if !$porn.categoryTargets.mmf>><<set $porn.categoryTargets.mmf = random(5, 8)>><</if>>
<<if ndef $porn.categoryTargets.mff>><<set $porn.categoryTargets.mff = random(5, 8)>><</if>>
<<if ndef $porn.categoryTargets.lesbian>><<set $porn.categoryTargets.lesbian = random(4, 6)>><</if>>
<<if $porn.pendingUnlock === "dp" || $porn.pendingUnlock === "spitroast">><<set $porn.pendingUnlock = "mmf">><</if>>
<<if $porn.selectedCategory === "dp" || $porn.selectedCategory === "spitroast">><<set $porn.selectedCategory = "mmf">><</if>>
<<if $porn.pendingUnlock === "group">><<set $porn.pendingUnlock = "mff">><</if>>
<<if $porn.selectedCategory === "group">><<set $porn.selectedCategory = "mff">><</if>>
<<if $porn.categoriesUnlocked.includes("dp") || $porn.categoriesUnlocked.includes("spitroast")>>
<<if !$porn.categoriesUnlocked.includes("mmf")>><<run $porn.categoriesUnlocked.push("mmf")>><</if>>
<</if>>
<<if $porn.categoriesUnlocked.includes("group")>>
<<if !$porn.categoriesUnlocked.includes("mff")>><<run $porn.categoriesUnlocked.push("mff")>><</if>>
<</if>>
<<run $porn.categoriesUnlocked = $porn.categoriesUnlocked.filter(function(_c){ return _c !== "dp" && _c !== "spitroast"; })>>
<<run $porn.categoriesUnlocked = $porn.categoriesUnlocked.filter(function(_c){ return _c !== "group"; })>>
<<if def $porn.categoriesProgressedToday>>
<<run $porn.categoriesProgressedToday = $porn.categoriesProgressedToday.map(function(_c){ return (_c === "dp" || _c === "spitroast") ? "mmf" : _c; })>>
<<run $porn.categoriesProgressedToday = $porn.categoriesProgressedToday.map(function(_c){ return _c === "group" ? "mff" : _c; })>>
<<run $porn.categoriesProgressedToday = [...new Set($porn.categoriesProgressedToday)]>>
<</if>>
<<if ($porn.categoryWatchCounts.group || 0) gt 0>>
<<set $porn.categoryWatchCounts.mff = Math.max($porn.categoryWatchCounts.mff || 0, $porn.categoryWatchCounts.group || 0)>>
<</if>>
<<if ($porn.categoryProgressCounts.group || 0) gt 0>>
<<set $porn.categoryProgressCounts.mff = Math.max($porn.categoryProgressCounts.mff || 0, $porn.categoryProgressCounts.group || 0)>>
<</if>>
<<if $porn.categoryFirstWatchSeen.group>>
<<set $porn.categoryFirstWatchSeen.mff = true>>
<</if>>
<<if $porn.categoryTargets.group>>
<<set $porn.categoryTargets.mff = $porn.categoryTargets.mff || $porn.categoryTargets.group>>
<</if>>
<<unset $porn.categoryWatchCounts.dp>>
<<unset $porn.categoryWatchCounts.spitroast>>
<<unset $porn.categoryProgressCounts.dp>>
<<unset $porn.categoryProgressCounts.spitroast>>
<<unset $porn.categoryFirstWatchSeen.dp>>
<<unset $porn.categoryFirstWatchSeen.spitroast>>
<<unset $porn.categoryTargets.dp>>
<<unset $porn.categoryTargets.spitroast>>
<<unset $porn.categoryWatchCounts.group>>
<<unset $porn.categoryProgressCounts.group>>
<<unset $porn.categoryFirstWatchSeen.group>>
<<unset $porn.categoryTargets.group>>
<<if ndef $arousalDecayHourBuffer>><<set $arousalDecayHourBuffer = 0>><</if>>
<<if ndef $hospitalLastDoctorDayKey>><<set $hospitalLastDoctorDayKey = 0>><</if>>
<<if $flags.gymMembershipActive && ($gymMembershipDaysRemaining || 0) <= 0>>
<<set $flags.gymMembershipActive = false>>
<<set $gymMembershipDaysRemaining = 0>>
<</if>>
/* Ensure hospital nurse exists and is known on existing saves. */
<<if ndef $characters.nurse>><<domInclude "charNurseRachelBrooksHospital">><</if>>
<<if $characters.nurse>>
<<set $characters.nurse.known = true>>
<</if>>
/* Ensure hospital doctor exists on existing saves (new char id). */
<<if ndef $characters.doctorAdrianCole>><<domInclude "charDoctorAdrianColeHospital">><</if>>
/* Backward-compat key migration: doctor -> doctorAdrianCole */
<<if $characters.doctor && ndef $characters.doctorAdrianCole>>
<<set $characters.doctorAdrianCole = clone($characters.doctor)>>
<</if>>
<<if $characters.doctor>>
<<unset $characters.doctor>>
<</if>>
/* Wardrobe quick outfit slot migration: ensure exactly 5 slots for old saves. */
<<if ndef $wardrobe>><<set $wardrobe = {}>><</if>>
<<if ndef $wardrobe.outfits or !Array.isArray($wardrobe.outfits)>>
<<set $wardrobe.outfits = [null, null, null, null, null]>>
<<else>>
<<run $wardrobe.outfits = $wardrobe.outfits.slice(0, 5)>>
<<for _i range $wardrobe.outfits.length; _i lt 5; _i++>>
<<run $wardrobe.outfits.push(null)>>
<</for>>
<</if>>
/* If player already has a job, ensure manager contact exists in phone. */
<<if $job && $job.id && setup.jobs && setup.jobs[$job.id]>>
<<set _mJob = setup.jobs[$job.id]>>
<<set _bossId = _mJob.bossCharId || "dinerManager">>
<<if ndef $phoneContactsUnlocked>><<set $phoneContactsUnlocked = []>><</if>>
<<if !$phoneContactsUnlocked.includes(_bossId)>>
<<run $phoneContactsUnlocked.push(_bossId)>>
<</if>>
<<if ndef $phoneConversations>><<set $phoneConversations = {}>><</if>>
<<if !$phoneConversations[_bossId]>><<set $phoneConversations[_bossId] = []>><</if>>
<<if $phoneConversations[_bossId].length === 0>>
<<set _t = $timeSys || {}>>
<<set _msgTime = { day: _t.day, month: _t.month, year: _t.year, hour: _t.hour, minute: _t.minute }>>
<<run $phoneConversations[_bossId].push({
from: _bossId,
text: "This is Vince. Save this number. If you can't make a shift, message me before start time.",
time: _msgTime,
read: false
})>>
<</if>>
<</if>>
<</if>>
<</silently>>/* ==========================================
SAVE VERSION MIGRATION SYSTEM
Migration passages live in passages/0 - System/Migrations/
Each file: SaveMigration_v1.twee, SaveMigration_v2.twee, etc.
Registry is in storyJavaScript.js:
setup.CURRENT_SAVE_VERSION latest version number
setup.saveVersions[] version metadata array
Runner: window.runSaveVersion()
Called automatically after save load.
Loops from current $saveVersion to CURRENT_SAVE_VERSION.
HOW TO ADD A NEW VERSION:
─────────────────────────
1. Create Migrations/SaveMigration_vN.twee
2. Add entry to setup.saveVersions in storyJavaScript.js
3. Increment setup.CURRENT_SAVE_VERSION
MIGRATION PASSAGE RULES:
• Wrap content in <<silently>>
• Use <<if $saveVersion lt N>> guard for idempotency
• Use <<domInclude>> for character init passages
• Do NOT set $saveVersion the runner handles it
• Do NOT use [init] tag
VERSION HISTORY:
─────────────────
v0 - First release (no migrations needed)
v1 - trust-based work system migration fields
========================================== */
/* ==========================================
SEXUAL SYSTEM WIDGETS
For use in sex scenes - auto tracking
========================================== */
/* ==========================================
MAIN WIDGET: <<sexScene>>
Use this for complete auto-handling!
Usage: <<sexScene "vaginal" "brother">>
This automatically:
- Tracks everything (virginity, count, partner)
- Shows first-time narrative if applicable
- No if statements needed in scenes!
========================================== */
<<widget "sexScene">>
<<set _type = $args[0]>>
<<set _characterId = $args[1]>>
<<set _characterName = $characters[_characterId]?.name || _characterId>>
/* Check if first time BEFORE updating */
<<set _wasFirstTime = $sexual.virginity[_type]?.intact ?? true>>
/* Take virginity if first time */
<<if _wasFirstTime>>
<<set $sexual.virginity[_type] = {
intact: false,
takenBy: _characterId,
takenByName: _characterName,
date: "Day " + $timeSys.day
}>>
/* Update firsts */
<<if _type === "vaginal" && !$firsts.firstVaginal>>
<<set $firsts.firstVaginal = _characterName>>
<<elseif _type === "anal" && !$firsts.firstAnal>>
<<set $firsts.firstAnal = _characterName>>
<<elseif _type === "oral" && !$firsts.firstOralGiven>>
<<set $firsts.firstOralGiven = _characterName>>
<</if>>
/* AUTO FIRST-TIME NARRATIVE */
<<if _type === "vaginal">>
<div class="first-time-notice">A sharp pain... then warmth. Your virginity is gone forever. _characterName was your first.</div>
<<elseif _type === "anal">>
<div class="first-time-notice">The unfamiliar sensation overwhelms you. Your anal virginity is taken by _characterName.</div>
<<elseif _type === "oral">>
<div class="first-time-notice">Your first time giving oral... you're nervous but eager to please _characterName.</div>
<</if>>
<</if>>
/* Increment count */
<<set $sexual.counts[_type] = ($sexual.counts[_type] || 0) + 1>>
/* Track partner */
<<if !$sexual.partnerList>>
<<set $sexual.partnerList = []>>
<</if>>
<<set _wasNewPartner = !$sexual.partnerList.includes(_characterId)>>
<<if _wasNewPartner>>
<<set $sexual.partnerList.push(_characterId)>>
<<set $sexual.totalPartners = $sexual.partnerList.length>>
<</if>>
<</widget>>
/* ==========================================
SIMPLE WIDGET: <<sexAct>>
For manual control (no auto-narrative)
Returns _wasFirstTime and _wasNewPartner
========================================== */
<<widget "sexAct">>
<<set _type = $args[0]>>
<<set _characterId = $args[1]>>
<<set _characterName = $characters[_characterId]?.name || _characterId>>
<<set _wasFirstTime = $sexual.virginity[_type]?.intact ?? true>>
<<if _wasFirstTime>>
<<set $sexual.virginity[_type] = {
intact: false,
takenBy: _characterId,
takenByName: _characterName,
date: "Day " + $timeSys.day
}>>
<<if _type === "vaginal" && !$firsts.firstVaginal>>
<<set $firsts.firstVaginal = _characterName>>
<<elseif _type === "anal" && !$firsts.firstAnal>>
<<set $firsts.firstAnal = _characterName>>
<<elseif _type === "oral" && !$firsts.firstOralGiven>>
<<set $firsts.firstOralGiven = _characterName>>
<</if>>
<</if>>
<<set $sexual.counts[_type] = ($sexual.counts[_type] || 0) + 1>>
<<if !$sexual.partnerList>>
<<set $sexual.partnerList = []>>
<</if>>
<<set _wasNewPartner = !$sexual.partnerList.includes(_characterId)>>
<<if _wasNewPartner>>
<<set $sexual.partnerList.push(_characterId)>>
<<set $sexual.totalPartners = $sexual.partnerList.length>>
<</if>>
<</widget>>
/* HANDJOB (no virginity) */
<<widget "handjob">>
<<set _characterId = $args[0]>>
<<set $sexual.counts.handjob = ($sexual.counts.handjob || 0) + 1>>
<</widget>>
/* CHECK IF VIRGIN */
<<widget "isVirgin">>
<<set _type = $args[0]>>
<<set _isVirgin = $sexual.virginity[_type]?.intact ?? true>>
<</widget>><<nobr>>
/* ==========================================
SHOPPING SYSTEM WIDGETS
Handles shop display, cart, and checkout
========================================== */
/* ==========================================
GET ITEM BY ID
Helper to find item from database
Usage: <<getItem "energy_drink">>
Returns: Sets _item to the item object or null
========================================== */
<<widget "getItem">>
<<set _itemId = $args[0]>>
<<set _item = null>>
<<for _category range Object.keys(setup.items)>>
<<for _i range setup.items[_category]>>
<<if _i.id === _itemId>>
<<set _item = _i>>
<<break>>
<</if>>
<</for>>
<<if _item !== null>><<break>><</if>>
<</for>>
<</widget>>
/* ==========================================
GET ITEMS BY IDS
Returns items from database by their IDs
Usage: <<getItemsByIds ["energy_drink", "coffee", "sandwich"]>>
Returns: Sets _shopItems to array of full item objects
Use in shop passages like:
<<set _itemList = ["energy_drink", "coffee"]>>
<<getItemsByIds _itemList>>
========================================== */
<<widget "getItemsByIds">>
<<set _itemIds = $args[0]>>
<<set _shopItems = []>>
<<for _itemId range _itemIds>>
<<getItem _itemId>>
<<if _item !== null>>
<<set _shopItems.push(_item)>>
<</if>>
<</for>>
<</widget>>
/* ==========================================
ADD TO CART
Adds item to shopping cart
Usage: <<addToCart "energy_drink" 1>>
========================================== */
<<widget "addToCart">>
<<set _itemId = $args[0]>>
<<set _qty = $args[1] || 1>>
<<getItem _itemId>>
<<if _item !== null>>
/* Check if already in cart */
<<set _found = false>>
<<for _idx, _cartItem range $shoppingCart>>
<<if _cartItem.id === _itemId>>
<<set $shoppingCart[_idx].quantity += _qty>>
<<set _found = true>>
<<break>>
<</if>>
<</for>>
/* If not found, add new entry */
<<if !_found>>
<<set $shoppingCart.push({
id: _itemId,
quantity: _qty,
price: _item.price
})>>
<</if>>
<</if>>
<</widget>>
/* ==========================================
REMOVE FROM CART
Removes item from shopping cart
Usage: <<removeFromCart "energy_drink">>
========================================== */
<<widget "removeFromCart">>
<<set _itemId = $args[0]>>
<<set $shoppingCart = $shoppingCart.filter(function(item) {
return item.id !== _itemId;
})>>
<</widget>>
/* ==========================================
UPDATE CART QUANTITY
Changes quantity of item in cart
Usage: <<updateCartQty "energy_drink" 3>>
========================================== */
<<widget "updateCartQty">>
<<set _itemId = $args[0]>>
<<set _newQty = $args[1]>>
<<if _newQty <= 0>>
<<removeFromCart _itemId>>
<<else>>
<<for _idx, _cartItem range $shoppingCart>>
<<if _cartItem.id === _itemId>>
<<set $shoppingCart[_idx].quantity = _newQty>>
<<break>>
<</if>>
<</for>>
<</if>>
<</widget>>
/* ==========================================
GET CART TOTAL
Calculates total price of cart
Returns: Sets _cartTotal to total price
========================================== */
<<widget "getCartTotal">>
<<set _cartTotal = 0>>
<<for _cartItem range $shoppingCart>>
<<set _cartTotal += _cartItem.price * _cartItem.quantity>>
<</for>>
<</widget>>
/* ==========================================
CLEAR CART
Empties the shopping cart
========================================== */
<<widget "clearCart">>
<<set $shoppingCart = []>>
<</widget>>
/* ==========================================
CHECKOUT WITH CASH
Processes purchase using cash
Returns: Sets _checkoutSuccess to true/false
========================================== */
<<widget "checkoutCash">>
<<getCartTotal>>
<<set _checkoutSuccess = false>>
<<if _cartTotal > 0 && $cashBalance >= _cartTotal>>
<<spendCash _cartTotal>>
<<if _transactionSuccess>>
/* Add items to inventory */
<<for _cartItem range $shoppingCart>>
<<addToInventory _cartItem.id _cartItem.quantity>>
<</for>>
<<clearCart>>
<<set _checkoutSuccess = true>>
<</if>>
<</if>>
<</widget>>
/* ==========================================
CHECKOUT WITH CARD
Processes purchase using bank card
Returns: Sets _checkoutSuccess to true/false
========================================== */
<<widget "checkoutCard">>
<<getCartTotal>>
<<set _checkoutSuccess = false>>
<<if _cartTotal > 0 && $bankBalance >= _cartTotal>>
<<spendCard _cartTotal>>
<<if _transactionSuccess>>
/* Add items to inventory */
<<for _cartItem range $shoppingCart>>
<<addToInventory _cartItem.id _cartItem.quantity>>
<</for>>
<<clearCart>>
<<set _checkoutSuccess = true>>
<</if>>
<</if>>
<</widget>>
/* ==========================================
ADD TO INVENTORY
Adds item to player's inventory
Usage: <<addToInventory "energy_drink" 2>>
========================================== */
<<widget "addToInventory">>
<<set _itemId = $args[0]>>
<<set _qty = $args[1] || 1>>
<<getItem _itemId>>
<<if _item !== null && _item.maxUses>>
<<set _qty = _qty * _item.maxUses>>
<</if>>
/* Check if already in inventory */
<<set _found = false>>
<<for _idx, _invItem range $inventory>>
<<if _invItem.id === _itemId>>
<<set $inventory[_idx].quantity += _qty>>
<<set _found = true>>
<<break>>
<</if>>
<</for>>
/* If not found, add new entry */
<<if !_found>>
<<set $inventory.push({
id: _itemId,
quantity: _qty
})>>
<</if>>
<</widget>>
/* ==========================================
REMOVE FROM INVENTORY
Removes item from player's inventory
Usage: <<removeFromInventory "energy_drink" 1>>
========================================== */
<<widget "removeFromInventory">>
<<set _itemId = $args[0]>>
<<set _qty = $args[1] || 1>>
<<for _idx, _invItem range $inventory>>
<<if _invItem.id === _itemId>>
<<set $inventory[_idx].quantity -= _qty>>
<<if $inventory[_idx].quantity <= 0>>
<<set $inventory.deleteAt(_idx)>>
<</if>>
<<break>>
<</if>>
<</for>>
<</widget>>
/* ==========================================
USE ITEM
Uses a consumable item and applies effects
Usage: <<useItem "energy_drink">>
Returns: Sets _useSuccess to true/false
========================================== */
<<widget "useItem">>
<<set _itemId = $args[0]>>
<<set _useSuccess = false>>
/* Check if in inventory */
<<set _hasItem = false>>
<<for _invItem range $inventory>>
<<if _invItem.id === _itemId && _invItem.quantity > 0>>
<<set _hasItem = true>>
<<break>>
<</if>>
<</for>>
<<if _hasItem>>
<<getItem _itemId>>
<<if _item !== null && _item.category === "consumable">>
/* Apply effects */
<<for _effect range _item.effects>>
<<if _effect.type === "instant">>
<<set State.variables[_effect.stat] += _effect.value>>
<</if>>
<</for>>
/* Remove from inventory */
<<removeFromInventory _itemId 1>>
<<set _useSuccess = true>>
<</if>>
<</if>>
<</widget>>
<</nobr>>
<<nobr>>
/* ==========================================
SKILL DECAY WIDGETS
Hybrid decay system with usage tracking
========================================== */
/* TRACK SKILL USAGE */
/* Usage: <<trackSkillUsage "mental" "research">> */
<<widget "trackSkillUsage">>
<<set _category = $args[0]>>
<<set _skillName = $args[1]>>
/* Initialize skillUsageTracking if undefined (backward compatibility) */
<<if !$skillUsageTracking>>
<<set $skillUsageTracking = {
mental: {}, social: {}, physical: {},
creative: {}, technical: {}, practical: {}
}>>
<</if>>
/* Record current day as last usage */
<<if $skillUsageTracking[_category]>>
<<set $skillUsageTracking[_category][_skillName] = $timeSys.day>>
<</if>>
<</widget>>
/* APPLY SKILL DECAY */
/* Called daily from <<advanceDay>> */
/* Hybrid algorithm: category-based + level-based + usage tracking */
<<widget "applySkillDecay">>
/* Only if setting is enabled */
<<if !$gameSettings.skillDecay>>
<<return>>
<</if>>
/* Safety init for old saves */
<<if !$skillUsageTracking>>
<<set $skillUsageTracking = {
mental: {}, social: {}, physical: {},
creative: {}, technical: {}, practical: {}
}>>
<</if>>
/* Define category-based decay rates (days until -1) */
<<set _decayRates = {
mental: 10,
technical: 10,
social: 7,
creative: 7,
physical: 5,
practical: 5
}>>
/* Current day for usage check */
<<set _currentDay = $timeSys.day>>
/* Loop through all skill categories */
<<for _category, _skillsObj range $skills>>
<<set _baseDecayDays = _decayRates[_category] || 7>>
/* Loop through skills in this category */
<<for _skillName, _skillValue range _skillsObj>>
/* Skip if skill is 0 (can't decay below 0) */
<<if _skillValue <= 0>>
<<continue>>
<</if>>
/* Check last usage */
<<set _lastUsedDay = $skillUsageTracking[_category][_skillName] || 0>>
<<set _daysSinceUse = _currentDay - _lastUsedDay>>
/* If used in last 7 days, skip decay */
<<if _daysSinceUse < 7>>
<<continue>>
<</if>>
/* Calculate level-based multiplier */
<<set _levelMultiplier = 1.0>>
<<if _skillValue <= 30>>
<<set _levelMultiplier = 0.7>> /* Beginners: 10×0.7=7 days (faster decay) */
<<elseif _skillValue >= 71>>
<<set _levelMultiplier = 1.5>> /* Experts: 10×1.5=15 days (slower decay) */
<</if>>
/* Calculate actual decay threshold */
<<set _decayThreshold = Math.round(_baseDecayDays * _levelMultiplier)>>
/* Apply decay if threshold reached */
<<if _daysSinceUse >= _decayThreshold>>
/* Calculate how many points to decay */
<<set _decayAmount = Math.floor(_daysSinceUse / _decayThreshold)>>
/* Apply decay (minimum 0) */
<<set _newSkillValue = Math.max(0, _skillValue - _decayAmount)>>
<<set $skills[_category][_skillName] = _newSkillValue>>
<<set _displayName = _skillName.charAt(0).toUpperCase() + _skillName.slice(1)>>
<<run window.notifyWarning(_displayName + " decayed")>>
<</if>>
<</for>>
<</for>>
<</widget>>
<</nobr>><<nobr>>
<<set setup.sofiaTopics = setup.sofiaTopics || {}>>
<<set setup.sofiaTopics.common = setup.sofiaTopics.common || {}>>
<<set setup.sofiaTopics.common.level1 = {
"dinerRubys_common_afternoon": {
"boyfriend_mention": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Sofia is leaning on the counter, scrolling through her phone with one hand. She hasn't looked up since you walked in.<</narrative>>
<<dialog "dinerWaitress2">>My boyfriend just sent me the dumbest meme.<</dialog>>
<<dialog "player">>Sounds like a slow afternoon.<</dialog>>
<<dialog "dinerWaitress2">>It's always slow. He's the only thing keeping me sane.<</dialog>>
<<dialog "player">>That's a lot of pressure on one guy.<</dialog>>
<<dialog "dinerWaitress2">>He can handle it. He's cute like that.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Sofia is texting rapidly under the counter, barely hiding her phone behind a stack of napkins.<</narrative>>
<<dialog "dinerWaitress2">>Hold on. My boyfriend's having a crisis.<</dialog>>
<<dialog "player">>Everything okay?<</dialog>>
<<dialog "dinerWaitress2">>He can't find his car keys. Again. Third time this week.<</dialog>>
<<dialog "player">>Maybe he needs a key hook.<</dialog>>
<<dialog "dinerWaitress2">>I told him that. He doesn't listen to me about anything practical.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Sofia smiles at her phone screen, then puts it face-down on the counter when she notices you.<</narrative>>
<<dialog "dinerWaitress2">>Sorry, what? I was reading something.<</dialog>>
<<dialog "player">>I didn't say anything yet.<</dialog>>
<<dialog "dinerWaitress2">>Oh. Well, my boyfriend just called me his sunshine. Unprompted.<</dialog>>
<<dialog "player">>That's sweet, I guess.<</dialog>>
<<dialog "dinerWaitress2">>It is sweet. This job could never.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"boredom": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Sofia is staring at the clock on the wall, chin resting on both hands.<</narrative>>
<<dialog "dinerWaitress2">>I swear that clock is broken. It hasn't moved.<</dialog>>
<<dialog "player">>It's only two thirty.<</dialog>>
<<dialog "dinerWaitress2">>Exactly. I've been here since noon and nothing has happened.<</dialog>>
<<dialog "player">>Isn't that a good thing?<</dialog>>
<<dialog "dinerWaitress2">>No. Time goes slower when nothing happens. It's science.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Sofia is balancing a pen on her upper lip, staring at the ceiling.<</narrative>>
<<dialog "dinerWaitress2">>Do you ever just stand somewhere and forget why you exist?<</dialog>>
<<dialog "player">>That's a bit heavy for a Tuesday.<</dialog>>
<<dialog "dinerWaitress2">>It's not deep. I'm just bored. My brain shuts off after an hour here.<</dialog>>
<<dialog "player">>Maybe do some tables.<</dialog>>
<<dialog "dinerWaitress2">>I did one. That's my quota.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Sofia is wiping the same spot on the counter in slow circles, clearly not paying attention.<</narrative>>
<<dialog "dinerWaitress2">>I've been wiping this spot for ten minutes. It's clean. I don't care.<</dialog>>
<<dialog "player">>Looks like you're pretending to work.<</dialog>>
<<dialog "dinerWaitress2">>That's literally what I'm doing. Vince walks by, I wipe. He leaves, I stop.<</dialog>>
<<dialog "player">>Efficient system.<</dialog>>
<<dialog "dinerWaitress2">>Thank you. I've perfected it.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"diner_observations": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-7.webp">>
<<image _img "100%">>
<<narrative>>Sofia watches a couple by the window with narrowed eyes, like she's solving a puzzle.<</narrative>>
<<dialog "dinerWaitress2">>That woman has been stirring her coffee for five minutes. It's not getting better.<</dialog>>
<<dialog "player">>Maybe she just likes the ritual.<</dialog>>
<<dialog "dinerWaitress2">>Or she doesn't want to go home. I get it, honestly.<</dialog>>
<<dialog "player">>This place is your escape?<</dialog>>
<<dialog "dinerWaitress2">>Don't push it. But sometimes, yeah.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-8.webp">>
<<image _img "100%">>
<<narrative>>Sofia gestures toward the door with her chin as a man leaves without tipping.<</narrative>>
<<dialog "dinerWaitress2">>He comes every week and leaves nothing. Every single time.<</dialog>>
<<dialog "player">>You keep track?<</dialog>>
<<dialog "dinerWaitress2">>I keep a mental list. It's long.<</dialog>>
<<dialog "player">>Does it help?<</dialog>>
<<dialog "dinerWaitress2">>No. But it gives me something to do.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-9.webp">>
<<image _img "100%">>
<<narrative>>Sofia rearranges the sugar packets on the counter into specific color groups. She doesn't look up.<</narrative>>
<<dialog "dinerWaitress2">>I group them by color when I'm bored. Nobody notices but it helps.<</dialog>>
<<dialog "player">>Helps with what?<</dialog>>
<<dialog "dinerWaitress2">>The feeling that I'm somehow in control of something here.<</dialog>>
<<dialog "player">>That's unexpectedly deep.<</dialog>>
<<dialog "dinerWaitress2">>Don't tell anyone. I have a reputation for not caring.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
},
"dinerRubys_common_evening": {
"wanting_to_leave": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-1.webp">>
<<image _img "100%">>
<<narrative>>Sofia has her bag already slung over one shoulder, checking her phone near the door.<</narrative>>
<<dialog "dinerWaitress2">>I've got twenty minutes left. I'm counting.<</dialog>>
<<dialog "player">>Somewhere to be?<</dialog>>
<<dialog "dinerWaitress2">>Anywhere that isn't here. My couch, maybe. My boyfriend's couch. Any couch.<</dialog>>
<<dialog "player">>Big plans.<</dialog>>
<<dialog "dinerWaitress2">>The biggest. Horizontal plans.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-2.webp">>
<<image _img "100%">>
<<narrative>>Sofia leans against the register, eyes half-closed. The evening light makes her look even more checked out.<</narrative>>
<<dialog "dinerWaitress2">>If one more person orders coffee, I'm walking out.<</dialog>>
<<dialog "player">>It's a diner. People order coffee.<</dialog>>
<<dialog "dinerWaitress2">>At this hour? Go home. Drink your own coffee. Leave me alone.<</dialog>>
<<dialog "player">>You seem ready to clock out.<</dialog>>
<<dialog "dinerWaitress2">>I was ready at noon.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-3.webp">>
<<image _img "100%">>
<<narrative>>Sofia is stacking sugar packets into a tiny tower, knocking it over, then rebuilding it.<</narrative>>
<<dialog "dinerWaitress2">>This is what I've been reduced to. Sugar architecture.<</dialog>>
<<dialog "player">>Creative use of your shift.<</dialog>>
<<dialog "dinerWaitress2">>I should be home watching something. Instead I'm here, building monuments to boredom.<</dialog>>
<<dialog "player">>At least it's almost closing.<</dialog>>
<<dialog "dinerWaitress2">>Almost isn't close enough.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"gossip": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-4.webp">>
<<image _img "100%">>
<<narrative>>Sofia glances toward the kitchen, then leans in slightly.<</narrative>>
<<dialog "dinerWaitress2">>Jake's been texting someone all evening. He keeps smiling at his phone.<</dialog>>
<<dialog "player">>Maybe he's just in a good mood.<</dialog>>
<<dialog "dinerWaitress2">>Nobody's in a good mood here unless there's a reason. I bet it's a girl.<</dialog>>
<<dialog "player">>Or a funny video.<</dialog>>
<<dialog "dinerWaitress2">>No. That's a girl-smile. I know the difference. Trust me.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-5.webp">>
<<image _img "100%">>
<<narrative>>Sofia watches Emma pass with a tray, then rolls her eyes.<</narrative>>
<<dialog "dinerWaitress2">>Emma acts like she runs this place. She doesn't even have keys.<</dialog>>
<<dialog "player">>She seems pretty busy, at least.<</dialog>>
<<dialog "dinerWaitress2">>Being busy isn't a personality. Some of us have actual lives outside this building.<</dialog>>
<<dialog "player">>Harsh.<</dialog>>
<<dialog "dinerWaitress2">>It's not harsh. It's an observation.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-6.webp">>
<<image _img "100%">>
<<narrative>>Sofia picks at her nails, glancing at the couple sitting by the window.<</narrative>>
<<dialog "dinerWaitress2">>That couple's been here for an hour. They haven't talked once.<</dialog>>
<<dialog "player">>Maybe they're comfortable with silence.<</dialog>>
<<dialog "dinerWaitress2">>Or they hate each other. My boyfriend and I never shut up.<</dialog>>
<<dialog "player">>That's one way to look at it.<</dialog>>
<<dialog "dinerWaitress2">>The only way. Silence is suspicious.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.sofiaTopics = setup.sofiaTopics || {}>>
<<set setup.sofiaTopics.dishwasherDone = setup.sofiaTopics.dishwasherDone || {}>>
<<set setup.sofiaTopics.dishwasherDone.level1 = {
"dinerRubys_dishwasherDone_afternoon": {
"checked_out": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Sofia is already scrolling her phone openly, apron untied and hanging off one shoulder.<</narrative>>
<<dialog "dinerWaitress2">>Oh, you're done too? Lucky us.<</dialog>>
<<dialog "player">>Yeah, just finished up.<</dialog>>
<<dialog "dinerWaitress2">>I mentally clocked out an hour ago. My body just caught up.<</dialog>>
<<dialog "player">>At least the shift's over.<</dialog>>
<<dialog "dinerWaitress2">>Best sentence in the English language.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Sofia is fixing her hair in the reflection of the napkin dispenser, clearly done working.<</narrative>>
<<dialog "dinerWaitress2">>I look terrible. This place drains the life out of me.<</dialog>>
<<dialog "player">>You look fine.<</dialog>>
<<dialog "dinerWaitress2">>Fine isn't the goal. I looked amazing this morning. Now look at me.<</dialog>>
<<dialog "player">>Work does that.<</dialog>>
<<dialog "dinerWaitress2">>Work shouldn't be allowed to do that. There should be a law.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Sofia drops her order pad on the counter like she's surrendering a weapon.<</narrative>>
<<dialog "dinerWaitress2">>Done. Finished. The pad is dead to me.<</dialog>>
<<dialog "player">>Dramatic exit.<</dialog>>
<<dialog "dinerWaitress2">>Every shift deserves a dramatic exit. It's the only reward.<</dialog>>
<<dialog "player">>What about getting paid?<</dialog>>
<<dialog "dinerWaitress2">>That too. But mostly the dramatic exit.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"boyfriend_plans": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Sofia is texting with a faint smile, leaning against the booth closest to the exit.<</narrative>>
<<dialog "dinerWaitress2">>My boyfriend's five minutes away. He's never on time but today he is.<</dialog>>
<<dialog "player">>Special occasion?<</dialog>>
<<dialog "dinerWaitress2">>No, I just threatened to walk home if he was late again. Motivation works.<</dialog>>
<<dialog "player">>Effective strategy.<</dialog>>
<<dialog "dinerWaitress2">>I'm basically a life coach. For one person. Who I'm dating.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Sofia is gathering her things phone, lip gloss, a small mirror stuffing them into her bag.<</narrative>>
<<dialog "dinerWaitress2">>We're going to the mall after this. I need new earrings.<</dialog>>
<<dialog "player">>Sounds like a plan.<</dialog>>
<<dialog "dinerWaitress2">>He doesn't know yet. He thinks we're going straight home. Surprise shopping trip.<</dialog>>
<<dialog "player">>He's going to love that.<</dialog>>
<<dialog "dinerWaitress2">>He won't. But he'll carry the bags. That's love.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Sofia checks her phone, frowns, then types something short.<</narrative>>
<<dialog "dinerWaitress2">>He asked what I want for dinner. Like I haven't been thinking about it for six hours.<</dialog>>
<<dialog "player">>What'd you pick?<</dialog>>
<<dialog "dinerWaitress2">>Sushi. But I said "I don't care, you choose." Then I'll veto whatever he picks.<</dialog>>
<<dialog "player">>Classic move.<</dialog>>
<<dialog "dinerWaitress2">>It's a system. He'll figure it out eventually.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_dishwasherDone_evening": {
"wanting_to_leave": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-1.webp">>
<<image _img "100%">>
<<narrative>>Sofia has her jacket on already, hovering near the door like she's about to sprint.<</narrative>>
<<dialog "dinerWaitress2">>I can literally see my boyfriend's car from here. Freedom is twelve steps away.<</dialog>>
<<dialog "player">>Then go.<</dialog>>
<<dialog "dinerWaitress2">>I can't. Vince wants me to sign out on the sheet. The sheet. Like it's 1985.<</dialog>>
<<dialog "player">>It takes ten seconds.<</dialog>>
<<dialog "dinerWaitress2">>Ten seconds I'll never get back. Of my life. Gone.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-2.webp">>
<<image _img "100%">>
<<narrative>>Sofia leans against the register, eyes closed, breathing out slowly like she survived something.<</narrative>>
<<dialog "dinerWaitress2">>Another shift survived. I deserve a medal. Or a nap.<</dialog>>
<<dialog "player">>Nap's more realistic.<</dialog>>
<<dialog "dinerWaitress2">>I'll take both. My boyfriend's couch is calling me. It's the comfiest couch in existence.<</dialog>>
<<dialog "player">>Everyone says that about their couch.<</dialog>>
<<dialog "dinerWaitress2">>Theirs is wrong. Mine is right. Scientifically.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-3.webp">>
<<image _img "100%">>
<<narrative>>Sofia pulls her hair tie out and shakes her hair loose, already transforming into her off-duty self.<</narrative>>
<<dialog "dinerWaitress2">>I hate having my hair up. My head hurts the whole shift.<</dialog>>
<<dialog "player">>At least it's over now.<</dialog>>
<<dialog "dinerWaitress2">>My scalp will remember this. For days. It holds grudges.<</dialog>>
<<dialog "player">>Your scalp holds grudges?<</dialog>>
<<dialog "dinerWaitress2">>Everything about me holds grudges. It's a personality trait.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"evening_gossip": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-4.webp">>
<<image _img "100%">>
<<narrative>>Sofia lowers her voice, glancing toward the back where Jake is still working.<</narrative>>
<<dialog "dinerWaitress2">>Jake's been way too cheerful tonight. Something's up with him.<</dialog>>
<<dialog "player">>He's always cheerful.<</dialog>>
<<dialog "dinerWaitress2">>Not like this. He whistled while mopping. That's suspicious behavior.<</dialog>>
<<dialog "player">>Maybe he just had a good day.<</dialog>>
<<dialog "dinerWaitress2">>Nobody has a good day mopping. Mark my words. There's a girl involved.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-5.webp">>
<<image _img "100%">>
<<narrative>>Sofia is putting on lip gloss, using her phone as a mirror.<</narrative>>
<<dialog "dinerWaitress2">>Emma stayed late again. She does that to make the rest of us look bad.<</dialog>>
<<dialog "player">>Maybe she just has extra work.<</dialog>>
<<dialog "dinerWaitress2">>She creates extra work. Nobody asked her to reorganize the condiment station.<</dialog>>
<<dialog "player">>It did look kind of messy.<</dialog>>
<<dialog "dinerWaitress2">>Messy is a style. She wouldn't understand.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-6.webp">>
<<image _img "100%">>
<<narrative>>Sofia waves her hand dismissively toward the diner as she walks toward the exit.<</narrative>>
<<dialog "dinerWaitress2">>Vince complimented Tom's shirt today. A shirt. He never compliments anything I wear.<</dialog>>
<<dialog "player">>Do you want him to?<</dialog>>
<<dialog "dinerWaitress2">>No. But it's the principle. I have better style than Tom. Obviously.<</dialog>>
<<dialog "player">>Obviously.<</dialog>>
<<dialog "dinerWaitress2">>Thank you. At least someone gets it. Goodnight.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherDone_afternoon": {
"post_shift_phone": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Sofia is sitting on the break stool, feet up on a box, scrolling through her phone with total abandon. She's completely off-duty.<</narrative>>
<<dialog "dinerWaitress2">>I'm not moving. My shift is over and this stool is mine now.<</dialog>>
<<dialog "player">>Nobody's asking you to move.<</dialog>>
<<dialog "dinerWaitress2">>Good. Because I physically can't. My legs have unionized. They're on strike.<</dialog>>
<<dialog "player">>Sounds serious.<</dialog>>
<<dialog "dinerWaitress2">>It is. I'm sending my boyfriend updates on my condition. He says "lol."<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Sofia has her shoes half off, wiggling her toes while watching something on her phone with earbuds in.<</narrative>>
<<dialog "dinerWaitress2">>Oh, sorry. Didn't hear you. I'm watching a makeup tutorial.<</dialog>>
<<dialog "player">>In the storage room?<</dialog>>
<<dialog "dinerWaitress2">>Best place for it. Nobody comes back here. Except you, apparently.<</dialog>>
<<dialog "player">>Just passing through.<</dialog>>
<<dialog "dinerWaitress2">>Good. Don't tell Emma I'm still here. She'll give me a task.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Sofia takes a selfie with the storage shelves behind her, frowns, deletes it, tries again from a different angle.<</narrative>>
<<dialog "dinerWaitress2">>The aesthetic in here is terrible. But it's got that raw, industrial vibe. Maybe.<</dialog>>
<<dialog "player">>Are you taking photos in the storage room?<</dialog>>
<<dialog "dinerWaitress2">>For my story. "Post-shift glow." It sounds better than "hiding from responsibilities."<</dialog>>
<<dialog "player">>Creative rebranding.<</dialog>>
<<dialog "dinerWaitress2">>That's literally my whole life philosophy. Make it look intentional.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"ready_to_go": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Sofia is gathering her stuff slowly, checking each pocket of her bag like she's performing an inventory.<</narrative>>
<<dialog "dinerWaitress2">>Phone, keys, lip balm, gum. That's everything I need in life.<</dialog>>
<<dialog "player">>Minimal essentials.<</dialog>>
<<dialog "dinerWaitress2">>I'm a simple girl with simple needs. And a boyfriend who's late. Again.<</dialog>>
<<dialog "player">>Thought you trained him.<</dialog>>
<<dialog "dinerWaitress2">>Training is ongoing. It's a process. A lifelong, exhausting process.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Sofia sprays something from a small bottle onto her wrists, sniffs, and nods approvingly.<</narrative>>
<<dialog "dinerWaitress2">>I keep perfume in my bag for after shifts. Can't walk around smelling like grease.<</dialog>>
<<dialog "player">>That's actually smart.<</dialog>>
<<dialog "dinerWaitress2">>I'm full of smart ideas. People just don't notice because I'm also pretty.<</dialog>>
<<dialog "player">>Modest too.<</dialog>>
<<dialog "dinerWaitress2">>Modesty is for people with nothing to brag about.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Sofia peeks out the storage room door, checking if the coast is clear.<</narrative>>
<<dialog "dinerWaitress2">>Is Vince out there? I want to leave without the goodbye speech.<</dialog>>
<<dialog "player">>What goodbye speech?<</dialog>>
<<dialog "dinerWaitress2">>He does this thing where he says "good work today" but makes it sound like a warning.<</dialog>>
<<dialog "player">>Maybe he means it.<</dialog>>
<<dialog "dinerWaitress2">>He never means anything nice. I'm going through the back. Cover me.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.sofiaTopics = setup.sofiaTopics || {}>>
<<set setup.sofiaTopics.dishwasherOnBreak = setup.sofiaTopics.dishwasherOnBreak || {}>>
<<set setup.sofiaTopics.dishwasherOnBreak.level1 = {
"dinerRubys_dishwasherOnBreak_afternoon": {
"boyfriend_drama": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Sofia is typing furiously on her phone, her expression shifting between annoyed and amused.<</narrative>>
<<dialog "dinerWaitress2">>My boyfriend just liked some girl's photo. From two years ago.<</dialog>>
<<dialog "player">>That's a deep scroll.<</dialog>>
<<dialog "dinerWaitress2">>Exactly. Who does that accidentally? Nobody. It's intentional archaeology.<</dialog>>
<<dialog "player">>Maybe he was just bored.<</dialog>>
<<dialog "dinerWaitress2">>He better be. I'm sending him a very passive-aggressive heart emoji right now.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Sofia sighs and puts her phone face-down, then immediately picks it up again.<</narrative>>
<<dialog "dinerWaitress2">>He left me on read. For forty minutes.<</dialog>>
<<dialog "player">>Maybe he's busy.<</dialog>>
<<dialog "dinerWaitress2">>He's never busy. He works from home. He's sitting right there.<</dialog>>
<<dialog "player">>Could be in the bathroom.<</dialog>>
<<dialog "dinerWaitress2">>For forty minutes? I'm going to text him again. Just a question mark.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Sofia shows you her phone screen from a distance. You can't really read it.<</narrative>>
<<dialog "dinerWaitress2">>Look at this. He wants to go to his friend's thing on Saturday. Our Saturday.<</dialog>>
<<dialog "player">>You had plans?<</dialog>>
<<dialog "dinerWaitress2">>We always have plans. Even if we don't plan them. Saturday is ours.<</dialog>>
<<dialog "player">>You could go with him.<</dialog>>
<<dialog "dinerWaitress2">>To watch guys play video games? I'd rather be here. Almost.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"social_media": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Sofia angles her phone camera toward the window, trying to get the light right.<</narrative>>
<<dialog "dinerWaitress2">>The lighting in here is terrible for selfies. Always.<</dialog>>
<<dialog "player">>You're taking selfies at work?<</dialog>>
<<dialog "dinerWaitress2">>I'm taking selfies everywhere. That's called consistency. Branding.<</dialog>>
<<dialog "player">>Vince would love that explanation.<</dialog>>
<<dialog "dinerWaitress2">>Vince doesn't know what branding is. He still uses a flip phone.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Sofia scrolls through her feed, occasionally double-tapping the screen.<</narrative>>
<<dialog "dinerWaitress2">>This girl I went to school with is in Bali right now. Must be nice.<</dialog>>
<<dialog "player">>Bali's expensive.<</dialog>>
<<dialog "dinerWaitress2">>She's got a rich boyfriend. That's the real career path nobody talks about.<</dialog>>
<<dialog "player">>Thought you had a boyfriend.<</dialog>>
<<dialog "dinerWaitress2">>I do. He's sweet but he's not Bali-sweet. Yet.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Sofia frowns at her phone, then aggressively taps the screen.<</narrative>>
<<dialog "dinerWaitress2">>Someone unfollowed me. I can feel it. The number went down.<</dialog>>
<<dialog "player">>Does one follower really matter?<</dialog>>
<<dialog "dinerWaitress2">>It matters when I was at a nice round number. Now it's uneven.<</dialog>>
<<dialog "player">>You'll survive.<</dialog>>
<<dialog "dinerWaitress2">>Barely. This is the worst part of my day. Including work.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_dishwasherOnBreak_evening": {
"after_work_plans": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-1.webp">>
<<image _img "100%">>
<<narrative>>Sofia is leaning on the counter, chin in her hand, staring at the door like she's willing it to be closing time.<</narrative>>
<<dialog "dinerWaitress2">>My boyfriend's picking me up after this. We're getting takeout.<</dialog>>
<<dialog "player">>From here?<</dialog>>
<<dialog "dinerWaitress2">>Absolutely not. I can't eat here after working here. That's psychological damage.<</dialog>>
<<dialog "player">>Fair point.<</dialog>>
<<dialog "dinerWaitress2">>We're thinking Thai. Or whatever's closest to his car.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-2.webp">>
<<image _img "100%">>
<<narrative>>Sofia stretches her arms above her head with an exaggerated groan.<</narrative>>
<<dialog "dinerWaitress2">>After this I'm taking the longest shower of my life. I smell like fries.<</dialog>>
<<dialog "player">>Could be worse smells.<</dialog>>
<<dialog "dinerWaitress2">>Not when you're trying to look cute for your boyfriend. Fry grease isn't a perfume.<</dialog>>
<<dialog "player">>He probably doesn't care.<</dialog>>
<<dialog "dinerWaitress2">>He doesn't. But I do. Standards.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-3.webp">>
<<image _img "100%">>
<<narrative>>Sofia checks the time on her phone and does a small fist pump to herself.<</narrative>>
<<dialog "dinerWaitress2">>One hour left. Then freedom. Then Netflix.<</dialog>>
<<dialog "player">>Watching anything good?<</dialog>>
<<dialog "dinerWaitress2">>Some reality show my boyfriend hates. Which makes it even better.<</dialog>>
<<dialog "player">>You watch it to annoy him?<</dialog>>
<<dialog "dinerWaitress2">>Partly. Mostly I just like watching people worse off than me.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"complaining": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-4.webp">>
<<image _img "100%">>
<<narrative>>Sofia rubs the small of her back, wincing slightly.<</narrative>>
<<dialog "dinerWaitress2">>My feet are done. Completely done. They've filed for divorce from my body.<</dialog>>
<<dialog "player">>Long shift?<</dialog>>
<<dialog "dinerWaitress2">>Every shift is long when you're standing on concrete in bad shoes.<</dialog>>
<<dialog "player">>Get better shoes.<</dialog>>
<<dialog "dinerWaitress2">>They don't make cute ones with arch support. I've looked.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-5.webp">>
<<image _img "100%">>
<<narrative>>Sofia glares at a table of empty cups nobody's cleared.<</narrative>>
<<dialog "dinerWaitress2">>That table's been empty for twenty minutes. I'm not touching it.<</dialog>>
<<dialog "player">>Isn't that your section?<</dialog>>
<<dialog "dinerWaitress2">>Technically. But I'm on break. Technically.<</dialog>>
<<dialog "player">>Are you actually on break?<</dialog>>
<<dialog "dinerWaitress2">>I'm always spiritually on break.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaFrontEvening-6.webp">>
<<image _img "100%">>
<<narrative>>Sofia watches Vince walk past and immediately pretends to straighten a menu stand.<</narrative>>
<<dialog "dinerWaitress2">>He just did the walk-by. The silent inspection one.<</dialog>>
<<dialog "player">>Did he say anything?<</dialog>>
<<dialog "dinerWaitress2">>He never says anything. He just looks. That's worse.<</dialog>>
<<dialog "player">>At least you weren't on your phone.<</dialog>>
<<dialog "dinerWaitress2">>I hid it so fast. Olympic-level reflexes. That's my real talent.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherOnBreak_afternoon": {
"texting_boyfriend": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-1.webp">>
<<image _img "100%">>
<<narrative>>Sofia is sitting on a crate in the storage room, phone in both hands, typing fast. She barely glances up.<</narrative>>
<<dialog "dinerWaitress2">>Oh. Hey. I'm in the middle of something important.<</dialog>>
<<dialog "player">>Important like work important?<</dialog>>
<<dialog "dinerWaitress2">>Important like my boyfriend's planning dinner and I need to pick the restaurant before he picks wrong.<</dialog>>
<<dialog "player">>High stakes.<</dialog>>
<<dialog "dinerWaitress2">>You have no idea. Last time he chose a burger place. I can't eat burgers after working here.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-2.webp">>
<<image _img "100%">>
<<narrative>>Sofia is grinning at her phone, sitting cross-legged on a supply box. A coffee sits untouched beside her.<</narrative>>
<<dialog "dinerWaitress2">>He sent me a voice message. Want to hear it?<</dialog>>
<<dialog "player">>I'm good, thanks.<</dialog>>
<<dialog "dinerWaitress2">>Your loss. He has a really nice voice. Everyone says so.<</dialog>>
<<dialog "player">>I'll take your word for it.<</dialog>>
<<dialog "dinerWaitress2">>You should. I have great taste.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-3.webp">>
<<image _img "100%">>
<<narrative>>Sofia holds her phone above her head, trying to find a signal in the storage room.<</narrative>>
<<dialog "dinerWaitress2">>The signal in here is garbage. I can't send anything.<</dialog>>
<<dialog "player">>It's a storage room. Concrete walls.<</dialog>>
<<dialog "dinerWaitress2">>They should install a router back here. It's inhumane.<</dialog>>
<<dialog "player">>You could just go outside.<</dialog>>
<<dialog "dinerWaitress2">>And risk Vince seeing me? No. I'll suffer in silence. With no bars.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"work_complaints": [
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-4.webp">>
<<image _img "100%">>
<<narrative>>Sofia is leaning against the shelves, arms crossed, staring at nothing in particular.<</narrative>>
<<dialog "dinerWaitress2">>Emma told me I need to smile more at customers. Like I'm a greeting card.<</dialog>>
<<dialog "player">>She's probably just trying to help.<</dialog>>
<<dialog "dinerWaitress2">>She's trying to manage me. She's not my manager. Vince is, and even he gave up.<</dialog>>
<<dialog "player">>That's not really a win.<</dialog>>
<<dialog "dinerWaitress2">>It feels like one. Less people telling me what to do.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-5.webp">>
<<image _img "100%">>
<<narrative>>Sofia picks at a loose thread on her apron, not looking at you.<</narrative>>
<<dialog "dinerWaitress2">>I was supposed to get off at four today. Now it's six. Nobody asked me.<</dialog>>
<<dialog "player">>Did Vince change the schedule?<</dialog>>
<<dialog "dinerWaitress2">>He always does. Then acts like it was always the plan. Gaslighting, basically.<</dialog>>
<<dialog "player">>That's frustrating.<</dialog>>
<<dialog "dinerWaitress2">>It's my life. Frustrating is the default setting.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/sofia/level1/sofiaStorageAfternoon-6.webp">>
<<image _img "100%">>
<<narrative>>Sofia holds up her hands, showing chipped nail polish.<</narrative>>
<<dialog "dinerWaitress2">>Look at my nails. This job ruins everything pretty about me.<</dialog>>
<<dialog "player">>You could wear gloves.<</dialog>>
<<dialog "dinerWaitress2">>Latex gloves with this outfit? I'd rather chip. At least I suffer with style.<</dialog>>
<<dialog "player">>That's one way to look at it.<</dialog>>
<<dialog "dinerWaitress2">>The only way. Survival through aesthetics.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<set $hideTopbarNav = true>>
<<set $hideTopbarTimebox = true>>
<<set $hideTopbarNotifications = true>>
<<set $hideRightbar = true>>
<<set $hideTopbar = true>>
<<set $hideTopbarHamburger = true>>/* ==========================================
STAT CALCULATION WIDGETS
Derived stats, multipliers, gains
========================================== */
/* REQUIRE MIN ENERGY - Block action if energy too low */
/* Usage: <<requireMinEnergy 1 "fhCouch">> or <<requireMinEnergy 5>>, optional 3rd arg: custom warning text */
<<widget "requireMinEnergy">><<nobr>>
<<set _min = typeof $args[0] !== "undefined" ? parseInt($args[0]) : 1>>
<<set _returnPassage = $args[1] || $location || "fhLivingroom">>
<<set _energyWarn = ($args.length > 2 && $args[2] !== undefined && $args[2] !== "") ? $args[2] : "Not enough energy.">>
<<if (parseInt($energy || 0)) < _min>>
<<if (parseInt($energy || 0)) <= 0>>
<<if !$_navigatingBackward>>
<<set $collapseReturnTo = _returnPassage>>
<<set $collapseAtLocation = $location || _returnPassage>>
<<goto "energy_collapse">>
<</if>>
<<else>>
<<run window.notifyError(_energyWarn)>>
<<goto _returnPassage>>
<</if>>
<</if>>
<</nobr>><</widget>>
/* Returns whether an optional simulation stat is enabled by settings. */
/* Usage: <<isSimulationStatEnabled "hunger">> -> _return true/false */
<<widget "isSimulationStatEnabled">><<nobr>>
<<set _statName = String($args[0] || "")>>
<<set _settingKey = "">>
<<if _statName === "hunger">>
<<set _settingKey = "trackHunger">>
<<elseif _statName === "thirst">>
<<set _settingKey = "trackThirst">>
<<elseif _statName === "bladder">>
<<set _settingKey = "trackBladder">>
<<elseif _statName === "hygiene">>
<<set _settingKey = "hygieneRequirement">>
<</if>>
<<if _settingKey === "">>
<<set _return = true>>
<<else>>
<<set _return = !($gameSettings && $gameSettings[_settingKey] === false)>>
<</if>>
<</nobr>><</widget>>
/* RECALCULATE DERIVED STATS */
<<widget "recalculateStats">><<nobr>>
/* 0. Sync Body Measurements (Text -> CM) */
<<if def "updateBodyMeasurements">>
<<updateBodyMeasurements>>
<</if>>
/* 1. Fitness Calculation */
<<set $fitness = Math.round(($upperBody + $core + $lowerBody + $cardio) / 4)>>
/* Update clothing score from wardrobe system */
<<if def setup.calculateTotalLooks>>
<<set $clothingScore = setup.calculateTotalLooks()>>
<<else>>
<<set $clothingScore = 0>>
<</if>>
/* Health is a PRIMARY stat - not derived from others */
/* Health decreases via penalties in NeedsSystem when vitals are critical */
/* Here we just clamp it to valid range */
<<set $health = Math.max(0, Math.min(100, $health))>>
/* HEALTH CRISIS CHECK, route to hospital/faint based on threshold */
/* Skip during sleep, history nav, and while forced admission flow is already running. */
<<set _healthPass = passage()>>
<<if $health <= 15 and !$isSleeping and !$_navigatingBackward and !$hospitalAdmissionInProgress and
_healthPass !== "hospital_admission" and _healthPass !== "hospitalAdmission" and
_healthPass !== "hospital_family_visit" and _healthPass !== "hospitalFamilyVisit" and
_healthPass !== "health_faint" and _healthPass !== "healthFaint" and
_healthPass !== "energy_collapse" and _healthPass !== "energyCollapse" and
_healthPass !== "energy_collapse_nap" and _healthPass !== "energyCollapseNap">>
<<goto "hospital_admission">>
<<elseif $health <= 20 and !$isSleeping and !$_navigatingBackward and
_healthPass !== "health_faint" and _healthPass !== "healthFaint" and
_healthPass !== "hospital_admission" and _healthPass !== "hospitalAdmission" and
_healthPass !== "hospital_family_visit" and _healthPass !== "hospitalFamilyVisit" and
_healthPass !== "energy_collapse" and _healthPass !== "energyCollapse" and
_healthPass !== "energy_collapse_nap" and _healthPass !== "energyCollapseNap">>
<<goto "health_faint">>
<</if>>
/* Energy Max = base + fitness bonus */
<<set $energyMax = Math.round(Math.min(150, Math.max(80, 80 + ($fitness * 0.2))))>>
/* Update Body Appeal (if widget exists) */
<<if def "calculateBodyAppeal">>
<<calculateBodyAppeal>>
<</if>>
/* Normalize disabled simulation stats so hidden systems cannot keep stale values. */
<<if !$gameSettings.trackHunger>><<set $hunger = 0>><</if>>
<<if !$gameSettings.trackThirst>><<set $thirst = 0>><</if>>
<<if !$gameSettings.trackBladder>><<set $bladder = 0>><</if>>
<<if !$gameSettings.hygieneRequirement>><<set $hygiene = 100>><</if>>
/* Beauty (0-100) = overall body/face attractiveness. No base – derived from fitness (highest), body, face/hair/dental care. */
<<set _bodyScaled = ($body.appeal || 0) * 2>>
<<set _beauty = Math.min(100, ($fitness || 0) * 0.28 + _bodyScaled * 0.25 + ($appearance.faceCare || 0) * 0.20 + ($appearance.hairCare || 0) * 0.15 + ($appearance.dentalCare || 0) * 0.12)>>
/* Penalty when face + dental care are bad: even with hygiene 100, looks stay in 20–25 range */
<<set _careSum = ($appearance.faceCare || 0) + ($appearance.dentalCare || 0)>>
<<if _careSum < 25>>
<<set _beauty = _beauty * 0.12>>
<<elseif _careSum < 50>>
<<set _beauty = _beauty * 0.40>>
<</if>>
<<set $beauty = Math.round(Math.max(0, Math.min(100, _beauty)))>>
/* Makeup score (0–100) from state/style/quality */
<<set _style = ($makeup && $makeup.style) ? $makeup.style : 0>>
<<set _state = ($makeup && $makeup.state) ? $makeup.state : "off">>
<<set _quality = ($makeup && ($makeup.quality !== undefined)) ? $makeup.quality : 0>>
<<set _styleBase = (_style >= 1 && _style <= 5) ? (_style * 20) : 0>>
<<if _state === "off" || _style === 0>>
<<set _makeupScore = 0>>
<<elseif _quality >= 50 && _state === "fresh">>
<<set _makeupScore = _styleBase>>
<<else>>
<<set _makeupScore = Math.round(_styleBase * (_quality / 100))>>
<</if>>
<<set _makeupScore = Math.max(0, Math.min(100, _makeupScore))>>
<<set _hygieneLooksValue = $gameSettings.hygieneRequirement ? ($hygiene || 0) : 100>>
/* Looks = Beauty (50%) + Hygiene (10%) + Clothing (20%) + Makeup (20%) */
<<set $looks = Math.max(0, Math.min(100, Math.round(_beauty * 0.50 + _hygieneLooksValue * 0.10 + ($clothingScore || 0) * 0.20 + _makeupScore * 0.20)))>>
/* Confidence = charisma + looks */
<<set $confidence = Math.round(Math.min(100, Math.max(0, ($charisma * 0.5) + ($looks * 0.3))))>>
/* GLOBAL CLAMPING - Ensure stats stay within bounds */
<<set $energy = Math.max(0, Math.min($energyMax, $energy))>>
/* ENERGY COLLAPSE, depleted after clamp (any loseStat / time / needs path that calls recalculateStats) */
<<set _collapsePass = passage()>>
<<if $energy <= 0 && !$isSleeping && !$_navigatingBackward &&
_collapsePass !== "energy_collapse" && _collapsePass !== "energyCollapse" &&
_collapsePass !== "energy_collapse_nap" && _collapsePass !== "energyCollapseNap" &&
_collapsePass !== "energy_collapse_items" && _collapsePass !== "energyCollapseItems" &&
_collapsePass !== "energy_collapse_use" && _collapsePass !== "energyCollapseUse" &&
_collapsePass !== "health_faint" && _collapsePass !== "healthFaint">>
<<if !$collapseReturnTo>><<set $collapseReturnTo = $location || "fhBedroom">><</if>>
<<set $collapseAtLocation = $location || $collapseReturnTo>>
<<goto "energy_collapse">>
<</if>>
<<set $stress = Math.max(0, Math.min(100, $stress))>>
<<set _safeArousal = Number($arousal)>>
<<if isNaN(_safeArousal)>><<set _safeArousal = 0>><</if>>
<<set $arousal = Math.max(0, Math.min(100, _safeArousal))>> /* 100 cap per user request */
/* Needs */
<<set $hunger = Math.max(0, Math.min(100, $hunger))>>
<<set $thirst = Math.max(0, Math.min(100, $thirst))>>
<<set $bladder = Math.max(0, Math.min(100, $bladder))>>
<<set $hygiene = Math.max(0, Math.min(100, $hygiene))>>
/* Skills Clamping (Iterate over all categories) */
<<if $skills>>
<<for _cat range Object.keys($skills)>>
<<for _skill range Object.keys($skills[_cat])>>
<<set $skills[_cat][_skill] = Math.max(0, Math.min(100, $skills[_cat][_skill]))>>
<</for>>
<</for>>
<</if>>
/* Recalculate body type */
<<recalculateBodyType>>
/* Update Time Event Icon (Alarm) */
/* Checks manual events and quest-specific meal times */
<<if Macro.has("updateTimedEvents")>>
<<updateTimedEvents>>
<</if>>
/* TOPBAR NOTIFICATION THRESHOLDS (70% = critical) */
/* Stats where LOW is bad */
<<set $notificationEnergy = ($energy <= ($energyMax * 0.3)) ? 1 : 0>>
<<set $notificationBed = ($energy <= ($energyMax * 0.3)) ? 1 : 0>>
<<set $notificationHealth = ($health <= 30) ? 1 : 0>>
<<set $notificationMood = ($mood <= 30) ? 1 : 0>>
<<set $notificationHygiene = ($gameSettings.hygieneRequirement && $hygiene <= 30) ? 1 : 0>>
/* Stats where HIGH is bad (needs) */
<<set $notificationArousal = ($arousal >= 70) ? 1 : 0>>
<<set $notificationBladder = ($gameSettings.trackBladder && $bladder >= 70) ? 1 : 0>>
<<set $notificationThirst = ($gameSettings.trackThirst && $thirst >= 70) ? 1 : 0>>
<<set $notificationHunger = ($gameSettings.trackHunger && $hunger >= 70) ? 1 : 0>>
/* Appearance (Face): show icon when any of hair/face/dental care is below 25%; tooltip lists only the low ones */
<<set _hair = $appearance.hairCare || 0>>
<<set _face = $appearance.faceCare || 0>>
<<set _dental = $appearance.dentalCare || 0>>
<<set $notificationFace = (_hair < 25 || _face < 25 || _dental < 25) ? 1 : 0>>
<<set _faceText = "">>
<<if _hair < 25>><<set _faceText = _faceText + "Hair - Needs care\n">><</if>>
<<if _face < 25>><<set _faceText = _faceText + "Face - Needs care\n">><</if>>
<<if _dental < 25>><<set _faceText = _faceText + "Dental - Needs care\n">><</if>>
<<set $notificationFaceText = _faceText.trim()>>
/* Health notifications now handled directly in NeedsSystem when health drops */
/* Mood Warning */
<<if $mood <= 30>>
<<if typeof $_moodWarningShown === "undefined" || $_moodWarningShown !== true>>
<<run window.notifyWarning("Mood Low: " + $mood)>>
<<set $_moodWarningShown = true>>
<</if>>
<<else>>
<<set $_moodWarningShown = false>>
<</if>>
/* Energy Warning */
<<if $energy <= ($energyMax * 0.3)>>
<<if typeof $_energyWarningShown === "undefined" || $_energyWarningShown !== true>>
<<run window.notifyWarning("Energy Low: " + $energy)>>
<<set $_energyWarningShown = true>>
<</if>>
<<else>>
<<set $_energyWarningShown = false>>
<</if>>
/* Makeup Smeared Warning (makeup on but quality < 50 or state smeared) */
<<if _state !== "off" && _style >= 1 && (_quality < 50 || _state === "smeared")>>
<<if typeof $_makeupSmearedWarningShown === "undefined" || $_makeupSmearedWarningShown !== true>>
<<run window.notifyWarning("Your makeup has smeared.")>>
<<set $_makeupSmearedWarningShown = true>>
<</if>>
<<else>>
<<set $_makeupSmearedWarningShown = false>>
<</if>>
<</nobr>><</widget>>
/* GAIN STAT WITH MULTIPLIER */
/* Usage: <<gainStat "intelligence" 5>> for player stats */
/* Usage: <<gainStat "friendship" 5 "mother">> for character stats */
/* Usage: <<gainStat "cardio" 6 20>> for player stat capped at 20 (3rd arg number = maxCap) */
/* Fitness multiplier applies to: upperBody, core, lowerBody, cardio */
<<widget "gainStat">><<nobr>>
<<set _statName = $args[0]>>
<<set _baseAmount = Number($args[1])>>
<<set _arg3 = $args[2]>>
<<set _characterId = (typeof _arg3 === "string" ? _arg3 : null)>>
<<set _maxCap = (typeof _arg3 === "number" ? _arg3 : null)>>
<<if isNaN(_baseAmount)>><<return>><</if>>
<<isSimulationStatEnabled _statName>>
<<if !_return>><<return>><</if>>
/* If character ID is provided (must be string), delegate to gainCharacterStat */
<<if _characterId>>
<<gainCharacterStat _characterId _statName _baseAmount>>
<<else>>
/* Handle player stat */
<<set _multiplier = $statMultipliers[_statName] || 1.0>>
/* Apply fitness multiplier to body part stats */
<<set _fitnessStats = ["upperBody", "core", "lowerBody", "cardio"]>>
<<if _fitnessStats.includes(_statName)>>
<<set _fitnessMultiplier = $statMultipliers.fitness || 1.0>>
<<set _multiplier = _multiplier * _fitnessMultiplier>>
<</if>>
<<set _actualGain = _baseAmount * _multiplier>>
/* Own-property checks: avoid `def State.variables[_statName]` and Object.prototype in <<set>> (SC2/runtime issues) */
<<set _playerStatExists = typeof State.variables[_statName] !== "undefined">>
<<if _playerStatExists>>
<<set _maxVal = 100>>
<<if _maxCap>>
<<set _maxVal = _maxCap>>
<<elseif _statName === "energy" && def $energyMax>>
<<set _maxVal = $energyMax>>
<</if>>
<<set _current = Number(State.variables[_statName])>>
<<if isNaN(_current)>><<set _current = 0>><</if>>
<<set _newVal = Math.min(_maxVal, Math.max(0, _current + _actualGain))>>
<<set _realGain = _newVal - _current>>
<<set State.variables[_statName] = _newVal>>
<<if _realGain > 0>><<queueStatChange _statName _realGain>><</if>>
<</if>>
<</if>>
/* Note: recalculateStats is called by advanceTime after all changes */
<</nobr>><</widget>>
/* GAIN SKILL WITH CATEGORY MULTIPLIER */
/* Usage: <<gainSkill "mental" "research" 5>> */
/* Usage: <<gainSkill "physical" "yoga" 1 20>> - 4th arg = maxCap (cap skill at 20) */
/* Physical skills also grant small stat bonuses */
<<widget "gainSkill">><<nobr>>
<<set _category = $args[0]>>
<<set _skillName = $args[1]>>
<<set _baseAmount = $args[2]>>
<<set _maxCap = $args[3]>>
<<set _multiplier = $skillMultipliers[_category] || 1.0>>
<<set _actualGain = _baseAmount * _multiplier>>
<<if $skills[_category] && def $skills[_category][_skillName]>>
<<set _skillMax = (_maxCap ? Math.min(100, _maxCap) : 100)>>
<<set _currentSkill = $skills[_category][_skillName]>>
<<set _newSkillVal = Math.min(_skillMax, Math.max(0, _currentSkill + _actualGain))>>
<<set _actualGain = _newSkillVal - _currentSkill>>
<<set $skills[_category][_skillName] = _newSkillVal>>
<<notifySkillChange _skillName _actualGain>>
/* Auto-track skill usage for decay system */
<<trackSkillUsage _category _skillName>>
/* SKILL → STAT CONNECTIONS */
/* Physical skills grant fitness stat bonuses (25% of skill gain) */
<<set _statBonus = _actualGain * 0.25>>
<<if _skillName === "dance">>
<<gainStat "cardio" _statBonus _maxCap>>
<<gainStat "core" _statBonus _maxCap>>
<<gainStat "lowerBody" (_statBonus * 0.4) _maxCap>> /* +10% total ~ 40% of bonus share */
<<elseif _skillName === "yoga">>
<<gainStat "core" _statBonus _maxCap>>
<<gainStat "lowerBody" _statBonus _maxCap>>
<<elseif _skillName === "basketball">>
<<gainStat "cardio" _statBonus>>
<<gainStat "lowerBody" _statBonus>>
<<gainStat "upperBody" (_statBonus * 0.4)>>
<<elseif _skillName === "volleyball">>
<<gainStat "cardio" _statBonus>>
<<gainStat "upperBody" _statBonus>>
<<gainStat "core" (_statBonus * 0.4)>>
<<elseif _skillName === "football">>
<<gainStat "cardio" _statBonus>>
<<gainStat "lowerBody" _statBonus>>
<<gainStat "upperBody" (_statBonus * 0.4)>>
<<elseif _skillName === "swimming">>
/* Swimming: All-around but Cardio dominant */
<<gainStat "cardio" _statBonus>>
<<gainStat "upperBody" (_statBonus * 0.5)>>
<<gainStat "core" (_statBonus * 0.5)>>
<<gainStat "lowerBody" (_statBonus * 0.5)>>
<</if>>
/* Mental skills grant intelligence bonus (10% of skill gain) */
<<if _category === "mental">>
<<gainStat "intelligence" (_actualGain * 0.1)>>
<</if>>
/* Social skills grant charisma bonus (10% of skill gain) */
<<if _category === "social">>
<<gainStat "charisma" (_actualGain * 0.1)>>
<</if>>
/* Creative skills grant creativity bonus (10% of skill gain) */
<<if _category === "creative">>
<<gainStat "creativity" (_actualGain * 0.1)>>
<</if>>
/* Technical skills grant focus bonus (10% of skill gain) */
<<if _category === "technical">>
<<gainStat "focus" (_actualGain * 0.1)>>
<</if>>
/* Practical skills grant willpower bonus (10% of skill gain) */
<<if _category === "practical">>
<<gainStat "willpower" (_actualGain * 0.1)>>
<</if>>
<</if>>
<</nobr>><</widget>>
/* GAIN SEXUAL SKILL */
/* Usage: <<gainSexualSkill "oral" 5>> */
/* Applies sensitivity multiplier */
<<widget "gainSexualSkill">><<nobr>>
<<set _skillName = $args[0]>>
<<set _baseAmount = $args[1]>>
<<set _multiplier = $statMultipliers.sensitivity || 1.0>>
<<set _actualGain = _baseAmount * _multiplier>>
<<if def $sexual.skills[_skillName]>>
<<set $sexual.skills[_skillName] = Math.min(100, Math.max(0, $sexual.skills[_skillName] + _actualGain))>>
<</if>>
/* Increase experience */
<<set $sexual.experience += _actualGain>>
<</nobr>><</widget>>
/* LOSE STAT (with multiplier) */
/* Usage: <<loseStat "energy" 10>> for player stats */
/* Usage: <<loseStat "friendship" 5 "mother">> for character stats */
<<widget "loseStat">><<nobr>>
<<set _statName = $args[0]>>
<<set _baseAmount = Number($args[1])>>
<<set _characterId = $args[2]>>
<<if isNaN(_baseAmount)>><<return>><</if>>
<<set _negAmount = -1 * _baseAmount>>
<<isSimulationStatEnabled _statName>>
<<if !_return>><<return>><</if>>
/* If character ID is provided (must be string), delegate to loseCharacterStat */
<<if _characterId && typeof _characterId === "string">>
<<loseCharacterStat _characterId _statName _baseAmount>>
<<else>>
/* Handle player stat */
<<set _playerStatExists = typeof State.variables[_statName] !== "undefined">>
<<if _playerStatExists>>
<<set _current = Number(State.variables[_statName])>>
<<if isNaN(_current)>><<set _current = 0>><</if>>
<<set State.variables[_statName] = Math.min(100, Math.max(0, _current - _baseAmount))>>
<<queueStatChange _statName _negAmount>>
<</if>>
<</if>>
/* Note: recalculateStats is called by advanceTime after all changes */
<</nobr>><</widget>>
/* LOSE SKILL */
/* Usage: <<loseSkill "mental" "research" 5>> */
<<widget "loseSkill">><<nobr>>
<<set _category = $args[0]>>
<<set _skillName = $args[1]>>
<<set _amount = $args[2]>>
<<set _negAmount = -1 * _amount>>
<<if $skills[_category] && def $skills[_category][_skillName]>>
<<set $skills[_category][_skillName] = Math.min(100, Math.max(0, $skills[_category][_skillName] - _amount))>>
<<notifySkillChange _skillName _negAmount>>
<</if>>
<</nobr>><</widget>>
/* APPLY PROLOGUE TRAIT */
/* Usage: <<applyTrait _traitData>> */
<<widget "applyTrait">><<nobr>>
<<set _trait = $args[0]>>
/* Apply stat multipliers */
<<if _trait.statMultipliers>>
<<for _stat, _value range _trait.statMultipliers>>
<<set $statMultipliers[_stat] = ($statMultipliers[_stat] || 1.0) * _value>>
<</for>>
<</if>>
/* Apply skill category multipliers */
<<if _trait.skillCategoryMultipliers>>
<<for _category, _value range _trait.skillCategoryMultipliers>>
<<set $skillMultipliers[_category] = ($skillMultipliers[_category] || 1.0) * _value>>
<</for>>
<</if>>
/* Add to player traits */
<<set _effects = {}>>
<<if _trait.statMultipliers>>
<<set _effects = Object.assign(_effects, _trait.statMultipliers)>>
<</if>>
<<if _trait.skillCategoryMultipliers>>
<<set _effects = Object.assign(_effects, _trait.skillCategoryMultipliers)>>
<</if>>
<<run $characters.player.traits.push({
name: _trait.name,
icon: _trait.icon,
description: _trait.description,
effects: _effects
})>>
<</nobr>><</widget>>
/* GET CORRUPTION LEVEL (1-10) */
/* Usage: <<set _level = $corruptionLevel>> */
<<widget "getCorruptionLevel">><<nobr>>
<<set _corruptionLevel = $corruption>>
<</nobr>><</widget>>
/* GAIN CHARACTER STAT */
/* Usage: <<gainCharacterStat "mother" "friendship" 2>> */
/* Level stats (love/friendship/lust/trust) automatically level up when threshold is reached. */
/* Threshold config: $characters[charId].levelUpThresholds[stat][currentLevel], default 100 */
/* Max level config: setup.characterDefs[charId].maxLevels[stat], default 5 */
<<widget "gainCharacterStat">><<nobr>>
<<set _charId = $args[0]>>
<<set _statName = $args[1]>>
<<set _baseAmount = $args[2]>>
<<set _silentNotify = ($args[3] === true || $args[3] === "silent")>>
/* Safety checks */
<<if !_charId>>
/* silent */
<<elseif !State.variables.characters>>
/* silent */
<<elseif !State.variables.characters[_charId]>>
/* silent */
<<elseif !State.variables.characters[_charId].stats>>
/* silent */
<<elseif State.variables.characters[_charId].stats[_statName] === undefined>>
/* Stat does not exist on character, do nothing */
<<else>>
<<if _baseAmount !== 0 && ["love","friendship","lust","trust"].includes(_statName)>>
<<if def "touchCharacterRelation">>
<<touchCharacterRelation _charId>>
<</if>>
<</if>>
<<set _charData = $characters[_charId]>>
<<set _charDef = setup.characterDefs[_charId] || {}>>
<<set _stats = _charData.stats>>
<<set _levelKey = _statName + "Level">>
<<set _isLevelStat = ["love","friendship","lust","trust"].includes(_statName)>>
<<set _charName = _charDef.firstName || _charData.firstName || _charData.name || _charId>>
<<set _displayStat = _statName.charAt(0).toUpperCase() + _statName.slice(1)>>
<<if _isLevelStat && _stats[_levelKey] !== undefined>>
/* Level stat logic */
<<set _currentLevel = _stats[_levelKey]>>
/* Read maxLevels from setup.characterDefs first (version-safe), fallback to $characters for old saves */
<<set _maxLevel = (_charDef.maxLevels && _charDef.maxLevels[_statName] !== undefined) ? _charDef.maxLevels[_statName] : ((_charData.maxLevels && _charData.maxLevels[_statName] !== undefined) ? _charData.maxLevels[_statName] : 5)>>
<<set _thresholds = (_charData.levelUpThresholds && _charData.levelUpThresholds[_statName]) ? _charData.levelUpThresholds[_statName] : {}>>
<<set _threshold = (_thresholds[_currentLevel] !== undefined) ? _thresholds[_currentLevel] : 100>>
<<if _currentLevel >= _maxLevel>>
/* At max level, use versionCaps from setup.characterDefs first (version-safe), fallback to $characters */
<<set _vCaps = (_charDef.versionCaps && _charDef.versionCaps[_statName] !== undefined) ? _charDef.versionCaps[_statName] : ((_charData.versionCaps && _charData.versionCaps[_statName] !== undefined) ? _charData.versionCaps[_statName] : _threshold)>>
<<set $characters[_charId].stats[_statName] = Math.min(_vCaps, Math.max(0, _stats[_statName] + _baseAmount))>>
<<if _baseAmount > 0 && !_silentNotify>>
<<run window.notifySuccess(_charName + " " + _displayStat + " +" + _baseAmount)>>
<</if>>
<<else>>
<<set _newVal = Math.max(0, _stats[_statName] + _baseAmount)>>
<<if _newVal >= _threshold>>
/* Level up */
<<set $characters[_charId].stats[_levelKey] = _currentLevel + 1>>
<<set $characters[_charId].stats[_statName] = 0>>
<<if !_silentNotify>>
<<run window.notifySuccess(_charName + " " + _displayStat + " → Level " + (_currentLevel + 1) + "!")>>
<</if>>
<<else>>
/* Normal gain */
<<set $characters[_charId].stats[_statName] = _newVal>>
<<set _sign = (_baseAmount > 0) ? "+" : "">>
<<if !_silentNotify>>
<<run window.notifySuccess(_charName + " " + _displayStat + " " + _sign + _baseAmount)>>
<</if>>
<</if>>
<</if>>
<<else>>
/* Non-level stat, clamp 0-100 */
<<set $characters[_charId].stats[_statName] = Math.min(100, Math.max(0, _stats[_statName] + _baseAmount))>>
<<set _sign = (_baseAmount > 0) ? "+" : "">>
<<if !_silentNotify>>
<<run window.notifySuccess(_charName + " " + _displayStat + " " + _sign + _baseAmount)>>
<</if>>
<</if>>
<</if>>
<</nobr>><</widget>>
/* LOSE CHARACTER STAT */
/* Usage: <<loseCharacterStat "mother" "friendship" 5>> */
<<widget "loseCharacterStat">><<nobr>>
<<set _charId = $args[0]>>
<<set _statName = $args[1]>>
<<set _amount = $args[2]>>
<<gainCharacterStat _charId _statName (-1 * _amount)>>
<</nobr>><</widget>>
/* GET OBEDIENCE STATUS */
/* Returns: Dominant, Assertive, Neutral, Compliant, Submissive */
<<widget "getObedienceStatus">><<nobr>>
<<if $obedience < -50>>
<<set _obedienceStatus = "Dominant">>
<<elseif $obedience < -20>>
<<set _obedienceStatus = "Assertive">>
<<elseif $obedience < 20>>
<<set _obedienceStatus = "Neutral">>
<<elseif $obedience < 50>>
<<set _obedienceStatus = "Compliant">>
<<else>>
<<set _obedienceStatus = "Submissive">>
<</if>>
<</nobr>><</widget>>
/* ==========================================
STAT CHANGE NOTIFICATION SYSTEM
Centralized notification management
Usage:
1. Queue changes: <<queueStatChange "hunger" 5>>
2. Flush all at once: <<flushNotifications>>
Colors: success (green), warning (yellow), error (red)
========================================== */
/* INITIALIZE NOTIFICATION QUEUE */
/* Called at start of any stat-changing operation */
<<widget "initNotificationQueue">>
<<if typeof $pendingNotifications === "undefined">>
<<set $pendingNotifications = {}>>
<</if>>
/* Safety fallback: some builds can miss notification.js init order. */
<<run
if (typeof window.formatNotifyDelta !== "function") {
window.formatNotifyDelta = function (n) {
var v = Number(n);
if (!Number.isFinite(v)) v = 0;
return (v > 0 ? "+" : "") + v;
};
}
if (typeof window.notifyStatToast !== "function") {
window.notifyStatToast = function (type, statKey, message) {
var text = String(message || "");
if (type === "error" && typeof window.notifyError === "function") {
return window.notifyError(text);
}
if (type === "warning" && typeof window.notifyWarning === "function") {
return window.notifyWarning(text);
}
if (typeof window.notifySuccess === "function") {
return window.notifySuccess(text);
}
if (typeof window.notifyWarning === "function") {
return window.notifyWarning(text);
}
if (typeof console !== "undefined" && typeof console.warn === "function") {
console.warn("[notifyStatToast:fallback]", type, statKey, text);
}
};
}
>>
<</widget>>
/* QUEUE A STAT CHANGE */
/* Usage: <<queueStatChange "hunger" 5>> or <<queueStatChange "health" -10>> */
<<widget "queueStatChange">>
<<initNotificationQueue>>
<<set _statName = $args[0]>>
<<set _amount = $args[1]>>
<<if typeof window.isSimulationStatNotifyEnabled === "function">>
<<if !window.isSimulationStatNotifyEnabled(_statName)>><<return>><</if>>
<</if>>
/* Accumulate changes for the same stat */
<<if typeof $pendingNotifications[_statName] === "undefined">>
<<set $pendingNotifications[_statName] = 0>>
<</if>>
<<set $pendingNotifications[_statName] += _amount>>
<</widget>>
/* FLUSH ALL PENDING NOTIFICATIONS */
/* Shows combined notifications for each stat */
<<widget "flushNotifications">>
<<initNotificationQueue>>
<<for _stat, _totalChange range $pendingNotifications>>
/* Only show notification if there's an actual change */
<<set _change = Number(_totalChange)>>
<<if _change !== 0 && !isNaN(_change)>>
<<set _displayName = _stat.charAt(0).toUpperCase() + _stat.slice(1)>>
/* Determine notification type based on stat and direction */
/* Positive changes for "good" stats = success, negative = warning */
/* For "bad" stats like hunger/thirst/stress, reversed (increasing is bad) */
<<set _badStats = ["hunger", "thirst", "bladder", "stress"]>>
<<if _stat === "health">>
/* Health is special - always show as error when dropping */
<<if _change < 0>>
<<run window.notifyStatToast("error", _stat, _displayName + " " + window.formatNotifyDelta(_change))>>
<<else>>
<<run window.notifyStatToast("success", _stat, _displayName + " " + window.formatNotifyDelta(_change))>>
<</if>>
<<elseif _stat === "corruption">>
<<if _change > 0>>
<<run window.notifyCorruption("Corruption Gained")>>
<<else>>
<<run window.notifyStatToast("warning", _stat, _displayName + " " + window.formatNotifyDelta(_change))>>
<</if>>
<<elseif _badStats.includes(_stat)>>
/* Bad stats - increasing is bad, decreasing is good */
<<if _change > 0>>
<<run window.notifyStatToast("warning", _stat, _displayName + " " + window.formatNotifyDelta(_change))>>
<<else>>
<<run window.notifyStatToast("success", _stat, _displayName + " " + window.formatNotifyDelta(_change))>>
<</if>>
<<else>>
/* Good stats - increasing is good, decreasing is bad */
<<if _change > 0>>
<<run window.notifyStatToast("success", _stat, _displayName + " " + window.formatNotifyDelta(_change))>>
<<else>>
<<run window.notifyStatToast("warning", _stat, _displayName + " " + window.formatNotifyDelta(_change))>>
<</if>>
<</if>>
<</if>>
<</for>>
/* Clear the queue */
<<set $pendingNotifications = {}>>
/* Refresh topbar/rightbar so stat values update in same passage */
<<run $(document).trigger(':passagerender')>>
<</widget>>
/* NOTIFY STAT CHANGE IMMEDIATELY (no queue) */
/* Usage: <<notifyStatChange "energy" -5>> */
<<widget "notifyStatChange">>
<<set _statName = $args[0]>>
<<set _amount = $args[1]>>
<<set _displayName = _statName.charAt(0).toUpperCase() + _statName.slice(1)>>
<<set _sign = (_amount > 0) ? "+" : "">>
/* Determine notification type */
<<set _badStats = ["hunger", "thirst", "bladder", "stress"]>>
<<if _statName === "health">>
<<if _amount < 0>>
<<run window.notifyStatToast("error", _statName, _displayName + " " + window.formatNotifyDelta(_amount))>>
<<else>>
<<run window.notifyStatToast("success", _statName, _displayName + " " + window.formatNotifyDelta(_amount))>>
<</if>>
<<elseif _statName === "corruption">>
<<if _amount > 0>>
<<run window.notifyCorruption("Corruption Gained")>>
<<else>>
<<run window.notifyStatToast("warning", _statName, _displayName + " " + window.formatNotifyDelta(_amount))>>
<</if>>
<<elseif _badStats.includes(_statName)>>
<<if _amount > 0>>
<<run window.notifyStatToast("warning", _statName, _displayName + " " + window.formatNotifyDelta(_amount))>>
<<else>>
<<run window.notifyStatToast("success", _statName, _displayName + " " + window.formatNotifyDelta(_amount))>>
<</if>>
<<else>>
<<if _amount > 0>>
<<run window.notifyStatToast("success", _statName, _displayName + " " + window.formatNotifyDelta(_amount))>>
<<else>>
<<run window.notifyStatToast("warning", _statName, _displayName + " " + window.formatNotifyDelta(_amount))>>
<</if>>
<</if>>
<</widget>>
/* NOTIFY SKILL CHANGE */
/* Usage: <<notifySkillChange "research" 5>> */
<<widget "notifySkillChange">>
<<set _skillName = $args[0]>>
<<set _amount = $args[1]>>
<<set _displayName = _skillName.charAt(0).toUpperCase() + _skillName.slice(1)>>
<<if _amount > 0>>
<<run window.notifySuccess(_displayName + " " + window.formatNotifyDelta(_amount))>>
<<else>>
<<run window.notifyWarning(_displayName + " " + window.formatNotifyDelta(_amount))>>
<</if>>
<</widget>>
<<nobr>>
<<widget "getPeriod">>
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 and _hour < 12>>
<<set _period = "morning">>
<<elseif _hour >= 12 and _hour < 18>>
<<set _period = "afternoon">>
<<elseif _hour >= 18 and _hour < 22>>
<<set _period = "evening">>
<<else>>
<<set _period = "night">>
<</if>>
<<set _return = _period>>
<</widget>>
<<widget "formatTime">>
<<set _h = $timeSys.hour.toString().padStart(2, '0')>>
<<set _m = $timeSys.minute.toString().padStart(2, '0')>>
<<set _return = _h + ":" + _m>>
<</widget>>
<<widget "formatDate">>
<<set _monthName = $timeConfig.monthNames[$timeSys.month - 1]>>
<<set _return = $timeSys.day + " " + _monthName + " " + $timeSys.year>>
<</widget>>
<<widget "getWeekdayName">>
<<set _return = $timeConfig.weekdayNames[$timeSys.weekday]>>
<</widget>>
/* advanceTime: call LAST in a passage, after all <<loseStat>>/<<gainStat>>/<<set>> etc.
Optional mode arg:
- normal: full hourly need processing
- relax: skip passive hourly needs + passive energy drain (time passes without auto-decay)
So recalculateStats and topbar/rightbar refresh see the final state. */
<<widget "advanceTime">>
/* History back re-runs passage; skip or we'd advance time again on top of restored state */
<<if $_navigatingBackward>><<return>><</if>>
<<set _minutes = parseInt($args[0], 10) || 30>>
<<set _mode = (typeof $args[1] === "string" ? $args[1] : "normal")>>
<<set _prevTimeAdvanceMode = $timeAdvanceMode || "normal">>
<<set $timeAdvanceMode = _mode>>
<<set $timeSys.minute = (parseInt($timeSys.minute, 10) || 0) + _minutes>>
<<updateWardrobeWear _minutes>>
<<if $timeSys.minute >= 60>>
<<set _hours = Math.floor($timeSys.minute / 60)>>
<<set $timeSys.minute = $timeSys.minute % 60>>
<<set $timeSys.hour = (parseInt($timeSys.hour, 10) || 0) + _hours>>
/* Safety: ensure notify delta formatter exists before any flushNotifications call. */
<<run
if (typeof window.formatNotifyDelta !== "function") {
window.formatNotifyDelta = function (n) {
var v = Number(n);
if (!Number.isFinite(v)) v = 0;
return (v > 0 ? "+" : "") + v;
};
}
>>
/* Initialize notification queue */
<<initNotificationQueue>>
/* normal mode only: apply passive hourly need/energy decay */
/* Skip passive energy drain while asleep, sleep passages apply their own recovery after time advances */
<<for _i = 0; _i < _hours; _i++>>
<<if _mode !== "relax">>
<<increaseHunger 5>>
<<increaseThirst 5>>
<<increaseBladder 8>>
<<decreaseHygiene 5>>
<<if !$isSleeping>>
<<loseStat "energy" 5>>
<</if>>
<</if>>
<</for>>
/* Global arousal decay: every 2 full game hours, amount scales with current arousal tier. */
<<if ndef $arousalDecayHourBuffer>><<set $arousalDecayHourBuffer = 0>><</if>>
<<set $arousalDecayHourBuffer += _hours>>
<<set _arousalDecaySteps = Math.floor($arousalDecayHourBuffer / 2)>>
<<if _arousalDecaySteps > 0>>
/* Apply at most one decay per advanceTime call to avoid stacked drops (e.g. 12). */
<<if $arousal <= 30>>
/* 0-30: big drop */
<<decreaseArousal 6>>
<<elseif $arousal <= 65>>
/* 30-65: medium drop */
<<decreaseArousal 4>>
<<else>>
/* 65-100: small drop */
<<decreaseArousal 2>>
<</if>>
<<set $arousalDecayHourBuffer = $arousalDecayHourBuffer % 2>>
<</if>>
/* Fotogram – first-hours engagement spike */
<<updateFotogramEngagementHours _hours>>
/* Show all accumulated notifications as combined toasts */
<<flushNotifications>>
<</if>>
<<if $timeSys.hour >= 24>>
<<set _days = Math.floor($timeSys.hour / 24)>>
<<set $timeSys.hour = $timeSys.hour % 24>>
<<advanceDay _days>>
<</if>>
/* Update Time Event Icon (Alarm Notification) */
<<updateTimedEvents>>
/* Update character locations based on new time */
<<updateCharacterLocations>>
<<set $timeAdvanceMode = _prevTimeAdvanceMode>>
/* Makeup hourly decay: apply once per full hour elapsed */
<<if def "updateMakeupHourlyDecay">>
<<updateMakeupHourlyDecay>>
<</if>>
/* Do NOT <<goto>> here when closed, advanceTime often runs at the start of activity passages;
an immediate goto would skip narrative/video. Closed-location kick + notification live in
storyJavaScript.js (:passagedisplay + deferred setTimeout). */
/* Recalculate and clamp stats (hunger, thirst, bladder, notifications, etc.) */
<<recalculateStats>>
/* Refresh topbar/rightbar after time & stat changes */
<<run $(document).trigger(':passagerender')>>
<</widget>>
/* Track worn minutes + dirt for equipped clothing items. */
<<widget "updateWardrobeWear">>
<<set _minutes = parseInt($args[0], 10) || 0>>
<<if _minutes <= 0>><<return>><</if>>
<<if ndef $wardrobe>><<set $wardrobe = { equipped: {}, itemState: {}, wornToday: {}, laundryBasket: [] } >><</if>>
<<if ndef $wardrobe.equipped>><<set $wardrobe.equipped = {}>><</if>>
<<if ndef $wardrobe.itemState>><<set $wardrobe.itemState = {}>><</if>>
<<if ndef $wardrobe.wornToday>><<set $wardrobe.wornToday = {}>><</if>>
<<if ndef $wardrobe.laundryBasket>><<set $wardrobe.laundryBasket = []>><</if>>
<<run
(function() {
const S = State.variables;
const mins = Number(State.temporary._minutes) || 0;
if (mins <= 0) return;
const w = S.wardrobe;
const equipped = w.equipped || {};
/* Dirt and laundry basket only in your bedroom so public time cannot strand you in undress flows later. */
const allowSoil = S.location === 'fhBedroom';
const seen = new Set();
Object.values(equipped).forEach(function(itemId) {
if (!itemId || seen.has(itemId)) return;
seen.add(itemId);
const item = setup.getClothingById ? setup.getClothingById(itemId) : null;
if (!item) return;
if (!w.wornToday[itemId]) w.wornToday[itemId] = 0;
w.wornToday[itemId] += mins;
if (!allowSoil) return;
if (!w.itemState[itemId]) {
w.itemState[itemId] = {
durability: (typeof item.durability === 'number' ? item.durability : 100),
dirt: 0
};
}
const st = w.itemState[itemId];
st.dirt = Math.max(0, Math.min(100, (typeof st.dirt === 'number' ? st.dirt : 0) + (mins * 0.02)));
if (st.dirt > 60 && !w.laundryBasket.includes(itemId)) {
w.laundryBasket.push(itemId);
}
});
})();
>>
<</widget>>
/* Apply day-end durability and reset daily wear counters. */
<<widget "processWardrobeDayEnd">>
<<set _days = parseInt($args[0], 10) || 1>>
<<if _days <= 0>><<return>><</if>>
<<if ndef $wardrobe or ndef $wardrobe.itemState or ndef $wardrobe.wornToday>><<return>><</if>>
<<run
(function() {
const w = State.variables.wardrobe;
const dayCount = Number(State.temporary._days) || 0;
for (let d = 0; d < dayCount; d++) {
const worn = w.wornToday || {};
Object.keys(worn).forEach(itemId => {
const wornMinutes = worn[itemId] || 0;
if (wornMinutes < 60) return;
let loss = Math.floor(wornMinutes / 120);
if (loss <= 0) return;
const item = setup.getClothingById ? setup.getClothingById(itemId) : null;
const isPremium = !!(item && (item.quality === 'Premium' || (Array.isArray(item.tags) && item.tags.includes('luxury'))));
if (isPremium) {
loss = Math.max(1, Math.round(loss * 0.7));
}
if (!w.itemState[itemId]) {
w.itemState[itemId] = {
durability: (item && typeof item.durability === 'number' ? item.durability : 100),
dirt: 0
};
}
const st = w.itemState[itemId];
const currentDur = typeof st.durability === 'number' ? st.durability : 100;
st.durability = Math.max(0, currentDur - loss);
});
w.wornToday = {};
}
})();
>>
<</widget>>
/* Reset dirt on all wardrobe items with runtime state; clear laundry basket. Call after <<advanceTime>> for a wash activity so equipped items do not stay dirty from the minutes spent. */
<<widget "washAllWardrobeItems">>
<<run
(function() {
const w = State.variables.wardrobe;
if (!w) return;
if (!w.itemState || typeof w.itemState !== 'object') w.itemState = {};
Object.keys(w.itemState).forEach(function(id) {
const st = w.itemState[id];
if (st && typeof st === 'object') st.dirt = 0;
});
w.laundryBasket = [];
})();
>>
<</widget>>
/* addHeelsTravelMinutes: when navigating or walking with heels on, add travel minutes.
Every 60 accumulated minutes = +1 heels skill, capped by worn shoe's heelsSkillCap.
Call from navCard (travelTime) or walk activities (activity duration in minutes). */
<<widget "addHeelsTravelMinutes">>
<<set _minutes = parseInt($args[0], 10) || 0>>
<<if _minutes <= 0>><<return>><</if>>
<<if ndef $heelsTravelMinutes>><<set $heelsTravelMinutes to 0>><</if>>
<<if ndef $wardrobe>><<set $wardrobe = { equipped: {} }>><</if>>
<<if ndef $wardrobe.equipped>><<set $wardrobe.equipped = {}>><</if>>
<<set _shoeId to $wardrobe.equipped.shoes>>
<<if !_shoeId>><<return>><</if>>
<<set _item to setup.getClothingById(_shoeId)>>
<<run var it = _item; window._heelsCapSafe = (it != null && typeof it === 'object' && it.heelsSkillCap != null) ? it.heelsSkillCap : undefined>>
<<if ndef window._heelsCapSafe>><<return>><</if>>
<<set _cap to window._heelsCapSafe>>
<<run if (window._heelsCapSafe !== undefined) delete window._heelsCapSafe>>
<<if ndef $skills.physical>><<return>><</if>>
<<if ndef $skills.physical.heels>><<set $skills.physical.heels to 0>><</if>>
<<set $heelsTravelMinutes = $heelsTravelMinutes + _minutes>>
<<set _currentHeels to $skills.physical.heels>>
<<set _maxGain to _cap - _currentHeels>>
<<set _fullBlocks to Math.floor($heelsTravelMinutes / 60)>>
<<set _gain to Math.min(_maxGain, _fullBlocks)>>
<<if _gain > 0>>
<<set $skills.physical.heels = _currentHeels + _gain>>
<<set $heelsTravelMinutes = $heelsTravelMinutes - (_gain * 60)>>
<<trackSkillUsage "physical" "heels">>
<<notifySkillChange "heels" _gain>>
<</if>>
<</widget>>
<<widget "advanceDay">>
<<set _days = $args[0] || 1>>
<<set _jobLeaveY = $timeSys.year>>
<<set _jobLeaveM = $timeSys.month>>
<<set _jobLeaveD = $timeSys.day>>
<<set _oldWeekday = $timeSys.weekday>>
<<set $timeSys.day += _days>>
<<set $timeSys.weekday = ($timeSys.weekday + _days) % 7>>
<<set _maxDays = $timeConfig.monthDays[$timeSys.month - 1]>>
<<if $timeSys.month == 2>>
<<if ($timeSys.year % 4 == 0 and $timeSys.year % 100 != 0) or ($timeSys.year % 400 == 0)>>
<<set _maxDays = 29>>
<</if>>
<</if>>
<<if $timeSys.day > _maxDays>>
<<set $timeSys.day = $timeSys.day - _maxDays>>
<<set $timeSys.month += 1>>
<<if $timeSys.month > 12>>
<<set $timeSys.month = 1>>
<<set $timeSys.year += 1>>
<<set $timeSysYear = $timeSys.year>>
<</if>>
<</if>>
<<processWardrobeDayEnd _days>>
/* ==================== NEW SYSTEMS ==================== */
/* Hunger - Increase for each day that passed (sleeping overnight) */
<<if $gameSettings.trackHunger>>
<<for _d = 0; _d < _days; _d++>>
<<increaseHunger 30>> /* ~6 hours sleep = 30 hunger */
<</for>>
<</if>>
/* Skill Decay - Apply daily skill degradation */
<<if $gameSettings.skillDecay>>
<<applySkillDecay>>
<</if>>
/* Relationship Decay - unseen characters slowly drift away */
<<if $gameSettings.relationshipDecay>>
<<if def "applyRelationshipDecay">>
<<applyRelationshipDecay>>
<</if>>
<</if>>
/* Calorie & Weight - Update weight based on daily balance */
<<if $gameSettings.trackCalories>>
<<updateWeight>>
<<set $dailyCalorieIntake = 0>> /* Reset for new day */
<</if>>
/* Daily Exercise Reset */
<<set $dailyExercise = 0>>
/* Family Meals Reset - new day, new meals */
<<resetFamilyMeals>>
/* Reset Daily Activities */
<<set $daily.yogaDone = false>>
<<set $daily.danceDone = false>>
<<set $daily.jogDone = false>>
<<set $daily.hairCreamUsed = false>>
<<set $daily.faceCreamUsed = false>>
<<set $daily.dentalCareUsed = false>>
<<set $daily.magazineStatUsedToday = false>>
<<set $daily.bookMinutesRead = 0>>
<<set $daily.dishwashRushSurgeToday = false>>
<<set $daily.dishwashVinceCheckToday = false>>
<<set $daily.snoozeCount = 0>>
<<set $daily.gymUpperDone = false>>
<<set $daily.gymCoreDone = false>>
<<set $daily.gymLowerDone = false>>
<<set $daily.gymCardioDone = false>>
<<gymMembershipAdvanceDays _days>>
/* Job system: only run if player has an active job with valid state */
<<if $job && $job.id && $jobState>>
<<jobEnsureState>>
/* Job: record insufficient days before reset (hoursToday from calendar day _jobLeaveY/M/D) */
<<jobRecordInsufficientDay _days _oldWeekday _jobLeaveY _jobLeaveM _jobLeaveD>>
/* Job: reset shiftsToday for new day */
<<jobResetDaily>>
/* Job: check if player skipped Monday boss call - fire immediately */
<<jobCheckSkippedBossCall _days _oldWeekday>>
/* Job: pay day (Monday) - minHours check, pay, reset */
<<if $timeSys.weekday === 1>>
<<jobProcessPayDay>>
<</if>>
<</if>>
/* Weekly care bonuses: if new week (weekday 0), apply bonus for previous week then reset */
<<if ndef $weeklyHairCreamUses>><<set $weeklyHairCreamUses = 0>><</if>>
<<if ndef $weeklyFaceCreamUses>><<set $weeklyFaceCreamUses = 0>><</if>>
<<if ndef $weeklyDentalCareUses>><<set $weeklyDentalCareUses = 0>><</if>>
<<if $timeSys.weekday === 0>>
<<if $weeklyHairCreamUses >= 2>>
<<set $appearance.hairCare = Math.min(100, ($appearance.hairCare || 0) + 5)>>
<</if>>
<<set $weeklyHairCreamUses = 0>>
<<if $weeklyFaceCreamUses >= 2>>
<<set $appearance.faceCare = Math.min(100, ($appearance.faceCare || 0) + 5)>>
<</if>>
<<set $weeklyFaceCreamUses = 0>>
<<if $weeklyDentalCareUses >= 2>>
<<set $appearance.dentalCare = Math.min(100, ($appearance.dentalCare || 0) + 5)>>
<</if>>
<<set $weeklyDentalCareUses = 0>>
<</if>>
/* Daily Appearance Update (hair growth, messiness, care decay) */
<<if def "dailyAppearanceUpdate">>
<<for _d = 0; _d < _days; _d++>>
<<dailyAppearanceUpdate>>
<</for>>
<</if>>
/* Fotogram – engagement (likes, followers) for posts */
<<updateFotogramEngagement>>
<</widget>>
<<widget "setTime">>
<<set _hour = $args[0]>>
<<set _minute = $args[1] || 0>>
<<if _hour < $timeSys.hour or (_hour == $timeSys.hour and _minute < $timeSys.minute)>>
<<advanceDay 1>>
<</if>>
<<set $timeSys.hour = _hour>>
<<set $timeSys.minute = _minute>>
<</widget>>
<<widget "nextPeriod">>
<<getPeriod>>
<<switch _period>>
<<case "morning">>
<<setTime 12 0>>
<<case "afternoon">>
<<setTime 18 0>>
<<case "evening">>
<<setTime 22 0>>
<<case "night">>
<<setTime 6 0>>
<</switch>>
<</widget>>
<<widget "showTime">>
<<getPeriod>>
<<formatTime>>
<<formatDate>>
<<getWeekdayName>>
<<print _return3>> - <<print _return2>>, <<print _return>>
<</widget>>
/* ==========================================
TIMED EVENT ICON LOGIC
Updates the Alarm Notification icon in topbar
Called by: advanceTime
========================================== */
<<widget "updateTimedEvents">><<silently>>
<<set _shouldShowIcon = false>>
<<set _iconText = "Time Event">>
/* 1. Manual/Quest Override */
/* controlled via <<setTimedEvent "Description">> */
<<if $manualTimedEvent is true>>
<<set _shouldShowIcon = true>>
<<set _iconText = $manualTimedEventText || "Special Event">>
<</if>>
/* 2. Quest Events – only when quest has mealType (manual opt-in)
Use cases: group meals, "meet X at place Y at time Z", "be at work by time T".
Add mealType (e.g. "dinner") or use <<setTimedEvent "…">> for custom text. */
<<if !_shouldShowIcon and $questState and $questState.active>>
<<for _qid, _state range $questState.active>>
<<set _quest = setup.quests[_qid]>>
<<if _quest and _quest.mealType>>
<<set _shouldShowIcon = true>>
<<set _stageIndex = _state.stage>>
<<if _quest.stages and _quest.stages[_stageIndex]>>
<<set _iconText = _quest.stages[_stageIndex].desc>>
<</if>>
<<if !_iconText or _iconText == "Time Event">>
<<set _iconText = _quest.title || "Quest Event">>
<</if>>
<<break>>
<</if>>
<</for>>
<</if>>
/* 3. Update Topbar Variables */
<<if _shouldShowIcon>>
<<set $notificationAlarm = 1>>
<<set $notificationAlarmText = _iconText>>
<<else>>
<<set $notificationAlarm = 0>>
<<set $notificationAlarmText = "">>
<</if>>
<</silently>><</widget>>
/* Manual controls for quests */
/* Usage: <<setTimedEvent "Meeting with Sarah">> */
<<widget "setTimedEvent">>
<<set $manualTimedEvent = true>>
<<set $manualTimedEventText = $args[0] || "Event">>
<<updateTimedEvents>>
<</widget>>
<<widget "clearTimedEvent">>
<<set $manualTimedEvent = false>>
<<set $manualTimedEventText = "">>
<<updateTimedEvents>>
<</widget>>
/* ==========================================
UPDATE CHARACTER LOCATIONS
Updates all character locations based on
current time and their schedules
========================================== */
<<widget "updateCharacterLocations">>
<<nobr>>
/* Sunday=0: use "sunday" if character has it, else "weekend". Saturday=6: weekend. Mon-Fri: weekday */
<<set _scheduleType = ($timeSys.weekday === 0) ? "sunday" : (($timeSys.weekday === 6) ? "weekend" : "weekday")>>
<<set _currentHour = $timeSys.hour>>
/* Calculate current date as number for comparison (YYYYMMDD) */
<<set _currentDate = $timeSys.year * 10000 + $timeSys.month * 100 + $timeSys.day>>
<<for _charId, _charData range setup.schedules>>
/* Determine which schedule phase to use */
<<set _schedulePhase = null>>
/* Father: Check work start date */
<<if _charId === "father" && $importantDates && $importantDates.fatherWorkStart>>
<<set _workDate = $importantDates.fatherWorkStart.year * 10000 +
$importantDates.fatherWorkStart.month * 100 +
$importantDates.fatherWorkStart.day>>
<<set _schedulePhase = (_currentDate >= _workDate) ? "postWork" : "preWork">>
<</if>>
/* Brother: Check cyclical school calendar (vacation periods) */
<<if _charId === "brother" && setup.schoolCalendar && setup.schoolCalendar.vacations>>
<<set _isVacation = false>>
<<set _monthDay = $timeSys.month * 100 + $timeSys.day>>
<<for _vac range setup.schoolCalendar.vacations>>
<<set _startMD = _vac.startMonth * 100 + _vac.startDay>>
<<set _endMD = _vac.endMonth * 100 + _vac.endDay>>
/* Handle periods that cross year boundary (e.g., Dec-Jan) */
<<if _startMD <= _endMD>>
/* Normal period (same year) */
<<if _monthDay >= _startMD && _monthDay <= _endMD>>
<<set _isVacation = true>>
<</if>>
<<else>>
/* Cross-year period (e.g., Dec 20 - Jan 10) */
<<if _monthDay >= _startMD || _monthDay <= _endMD>>
<<set _isVacation = true>>
<</if>>
<</if>>
<</for>>
<<set _schedulePhase = _isVacation ? "vacation" : "school">>
<</if>>
/* Get schedule: nested (with phase) or flat (legacy). Sunday: use sunday if defined, else fallback to weekend */
<<if _schedulePhase && _charData[_schedulePhase]>>
<<set _schedule = _charData[_schedulePhase][_scheduleType]>>
<<if _scheduleType === "sunday" && !_schedule>>
<<set _schedule = _charData[_schedulePhase].weekend>>
<</if>>
<<else>>
<<set _schedule = _charData[_scheduleType]>>
<<if _scheduleType === "sunday" && !_schedule>>
<<set _schedule = _charData.weekend>>
<</if>>
<</if>>
<<if _schedule>>
/* Find the most recent time slot (look backwards) */
<<set _currentSlot = null>>
<<set _currentMinute = $timeSys.minute>>
<<for _slot range _schedule>>
<<set _slotMinute = _slot.minute || 0>>
/* Check if this slot's time has passed (hour + minute comparison) */
<<if _slot.hour < _currentHour || (_slot.hour === _currentHour && _slotMinute <= _currentMinute)>>
<<set _currentSlot = _slot>>
<</if>>
<</for>>
/* If no slot found (before first entry), use last slot from previous day */
<<if _currentSlot === null>>
<<set _currentSlot = _schedule[_schedule.length - 1]>>
<</if>>
/* Update character location and status */
<<if $characters[_charId]>>
<<set $characters[_charId].currentLocation = _currentSlot.location>>
<<set $characters[_charId].currentStatus = _currentSlot.status>>
<</if>>
<</if>>
<</for>>
/* Meetup override: at meetup time, character appears at appointment location (overwrite schedule) */
<<set _currentMinute = _currentHour * 60 + $timeSys.minute>>
<<for _apt range $phoneAppointments>>
<<if _apt && _apt.status === "pending" && _apt.time && _apt.charId>>
<<set _aptDate = (_apt.time.year || 0) * 10000 + (_apt.time.month || 0) * 100 + (_apt.time.day || 0)>>
<<set _aptMin = (_apt.time.hour || 0) * 60 + (_apt.time.minute || 0)>>
<<if _aptDate === _currentDate && _currentMinute >= _aptMin && _currentMinute <= _aptMin + 30>>
<<if $characters[_apt.charId]>>
<<set $characters[_apt.charId].currentLocation = _apt.location>>
<<set $characters[_apt.charId].currentStatus = "available">>
<</if>>
<</if>>
<</if>>
<</for>>
<</nobr>>
<</widget>>
<</nobr>>
<<nobr>>
<<set setup.tomTopics = setup.tomTopics || {}>>
<<set setup.tomTopics.common = setup.tomTopics.common || {}>>
<<set setup.tomTopics.common.level1 = {
"dinerRubys_common_morning": {
"neighborhood_talk": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-common-1.webp">>
<<image _img "100%">>
<<narrative>>Tom is sorting receipts behind the register, glancing up as you approach.<</narrative>>
<<dialog "dinerClerk">>Morning. You from around here?<</dialog>>
<<dialog "player">>Yeah, just down the road actually.<</dialog>>
<<dialog "dinerClerk">>Thought I recognized you. Small neighborhood.<</dialog>>
<<dialog "player">>Gets smaller every day.<</dialog>>
<<dialog "dinerClerk">>That's the charm of it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-common-2.webp">>
<<image _img "100%">>
<<narrative>>Tom leans on the counter, watching the street through the front window.<</narrative>>
<<dialog "dinerClerk">>They're fixing the sidewalk on Maple again.<</dialog>>
<<dialog "player">>Didn't they just do that last year?<</dialog>>
<<dialog "dinerClerk">>Yep. Cracked right through the winter.<</dialog>>
<<dialog "player">>Tax dollars hard at work.<</dialog>>
<<dialog "dinerClerk">>At least it gives us something to talk about.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-common-3.webp">>
<<image _img "100%">>
<<narrative>>Tom straightens a stack of menus near the register, nodding to you.<</narrative>>
<<dialog "dinerClerk">>New bakery opened two blocks down. Seen it?<</dialog>>
<<dialog "player">>The one with the green awning?<</dialog>>
<<dialog "dinerClerk">>That's the one. Haven't tried it yet.<</dialog>>
<<dialog "player">>Might be worth checking out.<</dialog>>
<<dialog "dinerClerk">>If their coffee's good, I'm in trouble.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"regulars_chat": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-common-4.webp">>
<<image _img "100%">>
<<narrative>>Tom tops off a coffee mug for the old man at the counter, then turns to you.<</narrative>>
<<dialog "dinerClerk">>That guy's been coming here since before me.<</dialog>>
<<dialog "player">>How long have you been here?<</dialog>>
<<dialog "dinerClerk">>Going on five years now.<</dialog>>
<<dialog "player">>That's a good run.<</dialog>>
<<dialog "dinerClerk">>He's got me beat by about twenty.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-common-5.webp">>
<<image _img "100%">>
<<narrative>>Tom slides a plate down the counter to a waiting customer, smooth and practiced.<</narrative>>
<<dialog "dinerClerk">>Mornings are mostly regulars. Easier that way.<</dialog>>
<<dialog "player">>You know everyone's order by heart?<</dialog>>
<<dialog "dinerClerk">>The usuals, yeah. Saves time.<</dialog>>
<<dialog "player">>That's kind of impressive.<</dialog>>
<<dialog "dinerClerk">>It's just repetition. Nothing fancy.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-common-6.webp">>
<<image _img "100%">>
<<narrative>>Tom wipes the counter in a slow circle, watching the morning crowd settle in.<</narrative>>
<<dialog "dinerClerk">>See the lady by the window? Comes every Tuesday.<</dialog>>
<<dialog "player">>Always the same spot?<</dialog>>
<<dialog "dinerClerk">>Same spot, same order. Pancakes and black coffee.<</dialog>>
<<dialog "player">>Creature of habit.<</dialog>>
<<dialog "dinerClerk">>Aren't we all.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_common_afternoon": {
"diner_history": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-1.webp">>
<<image _img "100%">>
<<narrative>>Tom is refilling the napkin dispensers, moving down the counter methodically.<</narrative>>
<<dialog "dinerClerk">>This place has been here since the sixties.<</dialog>>
<<dialog "player">>Really? It holds up pretty well.<</dialog>>
<<dialog "dinerClerk">>Vince keeps it running. Won't let anything fall apart.<</dialog>>
<<dialog "player">>He seems like that kind of guy.<</dialog>>
<<dialog "dinerClerk">>You have no idea.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-2.webp">>
<<image _img "100%">>
<<narrative>>Tom taps the old jukebox near the door as he passes it. It flickers but doesn't turn on.<</narrative>>
<<dialog "dinerClerk">>That thing hasn't worked in years.<</dialog>>
<<dialog "player">>Why keep it around then?<</dialog>>
<<dialog "dinerClerk">>Vince says it's part of the character.<</dialog>>
<<dialog "player">>Can't argue with that.<</dialog>>
<<dialog "dinerClerk">>I've learned not to argue with Vince in general.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-3.webp">>
<<image _img "100%">>
<<narrative>>Tom leans against the register, arms loosely crossed, during a quiet spell.<</narrative>>
<<dialog "dinerClerk">>Used to be a hardware store next door. Before my time.<</dialog>>
<<dialog "player">>What happened to it?<</dialog>>
<<dialog "dinerClerk">>Closed up. Now it's a laundromat.<</dialog>>
<<dialog "player">>The neighborhood changes, huh.<</dialog>>
<<dialog "dinerClerk">>Slowly. This place stays the same though.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"small_talk": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-4.webp">>
<<image _img "100%">>
<<narrative>>Tom is wiping down the counter, working around a half-empty coffee pot.<</narrative>>
<<dialog "dinerClerk">>Afternoon slump. Want some coffee?<</dialog>>
<<dialog "player">>Is it fresh or has it been sitting?<</dialog>>
<<dialog "dinerClerk">>Honest answer? It's been a while.<</dialog>>
<<dialog "player">>I'll take my chances.<</dialog>>
<<dialog "dinerClerk">>Brave. I respect that.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-5.webp">>
<<image _img "100%">>
<<narrative>>Tom adjusts the specials board behind him, erasing a smudged word.<</narrative>>
<<dialog "dinerClerk">>Quiet afternoon. I'm not complaining.<</dialog>>
<<dialog "player">>Enjoy it while it lasts?<</dialog>>
<<dialog "dinerClerk">>Exactly. Dinner rush hits and it's chaos.<</dialog>>
<<dialog "player">>Sounds like a different place entirely.<</dialog>>
<<dialog "dinerClerk">>Same place, different speed.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-6.webp">>
<<image _img "100%">>
<<narrative>>Tom sorts through a handful of coins from the tip jar, stacking them neatly.<</narrative>>
<<dialog "dinerClerk">>Weather's supposed to be nice this weekend.<</dialog>>
<<dialog "player">>Yeah? Got any plans for it?<</dialog>>
<<dialog "dinerClerk">>Maybe. Nothing locked in yet.<</dialog>>
<<dialog "player">>Keeping your options open.<</dialog>>
<<dialog "dinerClerk">>That's the smart move.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"counter_stories": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-7.webp">>
<<image _img "100%">>
<<narrative>>Tom refills a customer's water glass, then leans against the counter once they leave.<<narrative>>
<<dialog "dinerClerk">>Had a guy last week try to pay with a foreign coin. Genuine article.<<dialog>>
<<dialog "player">>What did you do with it?<<dialog>>
<<dialog "dinerClerk">>Kept it. Kind of cool actually.<<dialog>>
<<dialog "player">>That's a generous way to look at it.<<dialog>>
<<dialog "dinerClerk">>People are strange. Makes the shift interesting.<<dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-8.webp">>
<<image _img "100%">>
<<narrative>>Tom wipes the counter as a group of customers filters out, leaving the place quiet again.<<narrative>>
<<dialog "dinerClerk">>Rush lasted exactly forty minutes. Like clockwork.<<dialog>>
<<dialog "player">>You time these things?<<dialog>>
<<dialog "dinerClerk">>After five years you just know.<<dialog>>
<<dialog "player">>What happens in the next forty?<<dialog>>
<<dialog "dinerClerk">>Nothing. That's the good part.<<dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-common-9.webp">>
<<image _img "100%">>
<<narrative>>Tom props his elbow on the counter, watching the door lazily between tasks.<<narrative>>
<<dialog "dinerClerk">>Kid came in asking for a job today. Couldn't have been more than sixteen.<<dialog>>
<<dialog "player">>Did Vince talk to him?<<dialog>>
<<dialog "dinerClerk">>Sent him home with a slice of pie instead. Said come back when you're eighteen.<<dialog>>
<<dialog "player">>That's surprisingly decent of him.<<dialog>>
<<dialog "dinerClerk">>He has his moments.<<dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.tomTopics = setup.tomTopics || {}>>
<<set setup.tomTopics.dishwasherDone = setup.tomTopics.dishwasherDone || {}>>
<<set setup.tomTopics.dishwasherDone.level1 = {
"dinerRubys_dishwasherDone_morning": {
"closing_up_morning": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-done-1.webp">>
<<image _img "100%">>
<<narrative>>Tom is counting the register, fingers moving quick through the bills.<</narrative>>
<<dialog "dinerClerk">>Almost done here. Morning went fast.<</dialog>>
<<dialog "player">>Felt like it flew by honestly.<</dialog>>
<<dialog "dinerClerk">>Good shifts always do. It's the slow ones that drag.<</dialog>>
<<dialog "player">>True. Those last forever.<</dialog>>
<<dialog "dinerClerk">>Like watching paint dry with an apron on.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-done-2.webp">>
<<image _img "100%">>
<<narrative>>Tom wipes down the register keys with a damp cloth, methodical and thorough.<</narrative>>
<<dialog "dinerClerk">>You did good today. Held it together.<</dialog>>
<<dialog "player">>Thanks. Still getting the hang of things.<</dialog>>
<<dialog "dinerClerk">>It shows less than you think.<</dialog>>
<<dialog "player">>That's reassuring, actually.<</dialog>>
<<dialog "dinerClerk">>Give it another week. It'll feel natural.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-done-3.webp">>
<<image _img "100%">>
<<narrative>>Tom tucks a pen behind his ear and stretches his back.<</narrative>>
<<dialog "dinerClerk">>Register's balanced. That's always a relief.<</dialog>>
<<dialog "player">>Does it ever not balance?<</dialog>>
<<dialog "dinerClerk">>Once in a while. Vince gets tense about it.<</dialog>>
<<dialog "player">>I can imagine.<</dialog>>
<<dialog "dinerClerk">>You don't want to be around for that conversation.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
"post_shift_plans": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-done-4.webp">>
<<image _img "100%">>
<<narrative>>Tom unties his apron and folds it neatly on the counter.<</narrative>>
<<dialog "dinerClerk">>Free at last. Got the afternoon wide open.<</dialog>>
<<dialog "player">>Any plans or just winging it?<</dialog>>
<<dialog "dinerClerk">>Probably groceries. Maybe a nap first.<</dialog>>
<<dialog "player">>Nap wins every time.<</dialog>>
<<dialog "dinerClerk">>You understand priorities.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-done-5.webp">>
<<image _img "100%">>
<<narrative>>Tom checks his phone, types a quick reply, and slides it back in his pocket.<</narrative>>
<<dialog "dinerClerk">>Girlfriend wants to get lunch. Somewhere that's not here.<</dialog>>
<<dialog "player">>Can't blame her for that.<</dialog>>
<<dialog "dinerClerk">>Right? I eat here enough as it is.<</dialog>>
<<dialog "player">>Variety keeps things interesting.<</dialog>>
<<dialog "dinerClerk">>That's what she says. She's usually right.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-done-6.webp">>
<<image _img "100%">>
<<narrative>>Tom grabs his jacket from under the counter, shrugging it on.<</narrative>>
<<dialog "dinerClerk">>Heading out. Need to return some library books.<</dialog>>
<<dialog "player">>Didn't peg you as a library guy.<</dialog>>
<<dialog "dinerClerk">>Free books. What's not to like?<</dialog>>
<<dialog "player">>Good point.<</dialog>>
<<dialog "dinerClerk">>See you next shift. Take it easy.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_dishwasherDone_afternoon": {
"winding_down": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-done-1.webp">>
<<image _img "100%">>
<<narrative>>Tom leans on the counter, the afternoon light stretching across the empty tables.<</narrative>>
<<dialog "dinerClerk">>Afternoon crowd cleared out. Finally quiet.<</dialog>>
<<dialog "player">>It was busy for a while there.<</dialog>>
<<dialog "dinerClerk">>Two o'clock rush. Happens every time.<</dialog>>
<<dialog "player">>Like clockwork.<</dialog>>
<<dialog "dinerClerk">>Literally. You can set your watch by it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-done-2.webp">>
<<image _img "100%">>
<<narrative>>Tom stacks some cleaned menus and slides them into the holder by the register.<</narrative>>
<<dialog "dinerClerk">>Another one in the books. Not bad.<</dialog>>
<<dialog "player">>Felt pretty smooth today.<</dialog>>
<<dialog "dinerClerk">>No disasters. That's the bar around here.<</dialog>>
<<dialog "player">>Low bar but I'll take it.<</dialog>>
<<dialog "dinerClerk">>It's a realistic bar. That's what counts.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-done-3.webp">>
<<image _img "100%">>
<<narrative>>Tom rolls his sleeves down, signaling the end of his working mode.<</narrative>>
<<dialog "dinerClerk">>My feet are done. Completely done.<</dialog>>
<<dialog "player">>Standing all day is no joke.<</dialog>>
<<dialog "dinerClerk">>Five years and it never gets easier.<</dialog>>
<<dialog "player">>Maybe better shoes would help?<</dialog>>
<<dialog "dinerClerk">>My girlfriend keeps saying that. Maybe you're both right.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"evening_ahead": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-done-4.webp">>
<<image _img "100%">>
<<narrative>>Tom pulls out his phone and scrolls through what looks like a text thread.<</narrative>>
<<dialog "dinerClerk">>Girlfriend's making pasta tonight. Can't complain.<</dialog>>
<<dialog "player">>Home-cooked meal after a shift? That's the dream.<</dialog>>
<<dialog "dinerClerk">>She's getting pretty good at it too.<</dialog>>
<<dialog "player">>Lucky guy.<</dialog>>
<<dialog "dinerClerk">>Don't tell her that. She'll expect it every night.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-done-5.webp">>
<<image _img "100%">>
<<narrative>>Tom tosses his apron into the laundry bin behind the counter.<</narrative>>
<<dialog "dinerClerk">>Might go for a walk before heading home. Clear my head.<</dialog>>
<<dialog "player">>Nice out there today for it.<</dialog>>
<<dialog "dinerClerk">>Yeah. After being inside all day, you forget what air feels like.<</dialog>>
<<dialog "player">>Fresh air hits different after a shift.<</dialog>>
<<dialog "dinerClerk">>Like a reset button.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-done-6.webp">>
<<image _img "100%">>
<<narrative>>Tom finishes counting his tips, folding the bills into his pocket.<</narrative>>
<<dialog "dinerClerk">>Not a bad haul today. Tips were decent.<</dialog>>
<<dialog "player">>Good days make the job worth it.<</dialog>>
<<dialog "dinerClerk">>They do. Bad days I just try to forget.<</dialog>>
<<dialog "player">>Solid approach.<</dialog>>
<<dialog "dinerClerk">>See you tomorrow. Get some rest.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherDone_afternoon": {
"end_of_day_storage": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-done-1.webp">>
<<image _img "100%">>
<<narrative>>Tom sits on the stool in the back, nursing the last of his coffee. His apron hangs on a hook behind him.<</narrative>>
<<dialog "dinerClerk">>Just killing time before I head out.<</dialog>>
<<dialog "player">>Waiting for something?<</dialog>>
<<dialog "dinerClerk">>Girlfriend's picking me up. Car's in the shop.<</dialog>>
<<dialog "player">>Ah, that's annoying.<</dialog>>
<<dialog "dinerClerk">>Could be worse. At least I get a ride.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-done-2.webp">>
<<image _img "100%">>
<<narrative>>Tom leans against the supply shelves, arms crossed, looking content.<</narrative>>
<<dialog "dinerClerk">>Quiet back here after a shift. I like it.<</dialog>>
<<dialog "player">>Decompressing before going home?<</dialog>>
<<dialog "dinerClerk">>Exactly. Need the buffer zone.<</dialog>>
<<dialog "player">>I totally get that.<</dialog>>
<<dialog "dinerClerk">>Five minutes of nothing. Then back to real life.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-done-3.webp">>
<<image _img "100%">>
<<narrative>>Tom checks the supply shelves, running his fingers along the labels out of habit.<</narrative>>
<<dialog "dinerClerk">>We're low on napkins again. I'll tell Vince.<</dialog>>
<<dialog "player">>Always something to keep track of, huh?<</dialog>>
<<dialog "dinerClerk">>Even after clocking out, can't turn it off.<</dialog>>
<<dialog "player">>Sounds like you care about this place.<</dialog>>
<<dialog "dinerClerk">>Been here too long not to.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
"looking_forward": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-done-4.webp">>
<<image _img "100%">>
<<narrative>>Tom stretches on the stool, cracking his knuckles one at a time.<</narrative>>
<<dialog "dinerClerk">>Thinking about ordering takeout tonight. Ironic, right?<</dialog>>
<<dialog "player">>A diner guy ordering takeout? A little bit.<</dialog>>
<<dialog "dinerClerk">>Sometimes you just want someone else to make the food.<</dialog>>
<<dialog "player">>No judgment here.<</dialog>>
<<dialog "dinerClerk">>That's why I like you.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-done-5.webp">>
<<image _img "100%">>
<<narrative>>Tom finishes his coffee and sets the empty mug on a shelf.<</narrative>>
<<dialog "dinerClerk">>My day off is Thursday. Already counting.<</dialog>>
<<dialog "player">>Got something planned?<</dialog>>
<<dialog "dinerClerk">>Sleep. Just sleep. Maybe brunch if I wake up.<</dialog>>
<<dialog "player">>Simple but effective.<</dialog>>
<<dialog "dinerClerk">>The best plans usually are.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-done-6.webp">>
<<image _img "100%">>
<<narrative>>Tom pockets his phone and stands up from the stool, ready to leave.<</narrative>>
<<dialog "dinerClerk">>Alright. I'm officially off the clock.<</dialog>>
<<dialog "player">>Freedom feels good, doesn't it?<</dialog>>
<<dialog "dinerClerk">>Every single time. Never gets old.<</dialog>>
<<dialog "player">>Enjoy your evening, Tom.<</dialog>>
<<dialog "dinerClerk">>You too. Don't stay here too long.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.tomTopics = setup.tomTopics || {}>>
<<set setup.tomTopics.dishwasherOnBreak = setup.tomTopics.dishwasherOnBreak || {}>>
<<set setup.tomTopics.dishwasherOnBreak.level1 = {
"dinerRubys_dishwasherOnBreak_morning": {
"girlfriend_mention": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Tom leans on the register, checking his phone for a second before putting it away.<</narrative>>
<<dialog "dinerClerk">>My girlfriend texted. Wants dinner plans.<</dialog>>
<<dialog "player">>Already thinking about dinner at nine AM?<</dialog>>
<<dialog "dinerClerk">>She's a planner. I just go along with it.<</dialog>>
<<dialog "player">>Smart strategy.<</dialog>>
<<dialog "dinerClerk">>Years of practice.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Tom is organizing sugar packets in the caddy, sorting by color.<</narrative>>
<<dialog "dinerClerk">>My girlfriend tried making waffles yesterday. Smoke alarm went off.<</dialog>>
<<dialog "player">>Was she okay?<</dialog>>
<<dialog "dinerClerk">>Fine. The waffles weren't though.<</dialog>>
<<dialog "player">>At least she tried.<</dialog>>
<<dialog "dinerClerk">>That's what I told her. She didn't buy it.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Tom refills a ketchup bottle, squeezing one into another with practiced ease.<</narrative>>
<<dialog "dinerClerk">>Promised I'd pick up groceries after shift.<</dialog>>
<<dialog "player">>Big shopping trip?<</dialog>>
<<dialog "dinerClerk">>She sent a list. It's long.<</dialog>>
<<dialog "player">>Good luck with that.<</dialog>>
<<dialog "dinerClerk">>I'll need it. She's very specific about brands.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
"coffee_talk": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-onBreak-4.webp">>
<<image _img "100%">>
<<narrative>>Tom pours himself a quick half-cup of coffee behind the counter.<</narrative>>
<<dialog "dinerClerk">>Third cup today. Might be a record.<</dialog>>
<<dialog "player">>It's not even ten yet.<</dialog>>
<<dialog "dinerClerk">>Mornings hit different when you're up at five.<</dialog>>
<<dialog "player">>Five? That's brutal.<</dialog>>
<<dialog "dinerClerk">>You get used to it. Mostly.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-onBreak-5.webp">>
<<image _img "100%">>
<<narrative>>Tom holds his coffee mug with both hands, warming his fingers.<</narrative>>
<<dialog "dinerClerk">>You a coffee person or tea?<</dialog>>
<<dialog "player">>Coffee, definitely. Can't function without it.<</dialog>>
<<dialog "dinerClerk">>Same here. We go through pots fast around here.<</dialog>>
<<dialog "player">>I believe it.<</dialog>>
<<dialog "dinerClerk">>Help yourself anytime. Perk of the job.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontMorning-onBreak-6.webp">>
<<image _img "100%">>
<<narrative>>Tom takes a sip of coffee and winces slightly.<</narrative>>
<<dialog "dinerClerk">>I let that sit too long. It's bitter.<</dialog>>
<<dialog "player">>Pour it out and make a new one?<</dialog>>
<<dialog "dinerClerk">>Can't waste it. That's a Vince rule.<</dialog>>
<<dialog "player">>He has a rule about coffee?<</dialog>>
<<dialog "dinerClerk">>He has a rule about everything.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_dishwasherOnBreak_afternoon": {
"weekend_plans": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Tom wipes his hands on his apron, leaning back against the counter during a lull.<</narrative>>
<<dialog "dinerClerk">>Got any weekend plans?<</dialog>>
<<dialog "player">>Not really. Maybe just hang around.<</dialog>>
<<dialog "dinerClerk">>Same. Low-key weekends are underrated.<</dialog>>
<<dialog "player">>Agreed. No pressure that way.<</dialog>>
<<dialog "dinerClerk">>Exactly how I like it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Tom glances at the clock above the door, then back at you.<</narrative>>
<<dialog "dinerClerk">>Thinking about catching a movie this weekend.<</dialog>>
<<dialog "player">>Anything good out right now?<</dialog>>
<<dialog "dinerClerk">>No idea. That's half the fun though.<</dialog>>
<<dialog "player">>Just pick whatever's playing?<</dialog>>
<<dialog "dinerClerk">>Works every time. Low expectations, high reward.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Tom stretches his arms above his head, rolling his neck.<</narrative>>
<<dialog "dinerClerk">>My girlfriend wants to do a farmers market Saturday.<</dialog>>
<<dialog "player">>Those can be pretty nice actually.<</dialog>>
<<dialog "dinerClerk">>I mostly go for the food samples.<</dialog>>
<<dialog "player">>Honest man.<</dialog>>
<<dialog "dinerClerk">>Free cheese is free cheese.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
"something_he_read": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-onBreak-4.webp">>
<<image _img "100%">>
<<narrative>>Tom taps the counter absently, like he's thinking about something.<</narrative>>
<<dialog "dinerClerk">>Read this thing about how diners are disappearing.<</dialog>>
<<dialog "player">>Like closing down across the country?<</dialog>>
<<dialog "dinerClerk">>Yeah. Chains are pushing them out, they said.<</dialog>>
<<dialog "player">>This place seems pretty solid though.<</dialog>>
<<dialog "dinerClerk">>Hope so. Vince would go down swinging.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-onBreak-5.webp">>
<<image _img "100%">>
<<narrative>>Tom leans on his elbows at the counter, looking relaxed for once.<</narrative>>
<<dialog "dinerClerk">>Saw this article about people who quit office jobs for food trucks.<</dialog>>
<<dialog "player">>Tempting thought?<</dialog>>
<<dialog "dinerClerk">>Nah. I like having walls and a roof.<</dialog>>
<<dialog "player">>Fair point. Rain would be rough.<</dialog>>
<<dialog "dinerClerk">>Plus I'd miss the regulars.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomFrontAfternoon-onBreak-6.webp">>
<<image _img "100%">>
<<narrative>>Tom flips through a folded newspaper someone left on the counter.<</narrative>>
<<dialog "dinerClerk">>Somebody left the crossword half finished.<</dialog>>
<<dialog "player">>You gonna finish it?<</dialog>>
<<dialog "dinerClerk">>Tried. Got stuck on seven across.<</dialog>>
<<dialog "player">>What's the clue?<</dialog>>
<<dialog "dinerClerk">>Something about a Greek island. Not my thing.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysStorage_dishwasherOnBreak_afternoon": {
"break_room_chill": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Tom is sitting on the stool in the storage room, coffee in hand, feet up on a crate.<</narrative>>
<<dialog "dinerClerk">>Best part of the day right here.<</dialog>>
<<dialog "player">>The storage room? Really?<</dialog>>
<<dialog "dinerClerk">>Nobody bugs you back here. It's the principle.<</dialog>>
<<dialog "player">>I get that. Peace and quiet.<</dialog>>
<<dialog "dinerClerk">>Exactly. Pull up a crate if you want.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Tom sips his coffee slowly, leaning against the shelves stacked with supplies.<</narrative>>
<<dialog "dinerClerk">>You ever just sit and do nothing? Like on purpose?<</dialog>>
<<dialog "player">>More often than I probably should.<</dialog>>
<<dialog "dinerClerk">>My girlfriend calls it recharging. I call it lazy.<</dialog>>
<<dialog "player">>Recharging sounds better.<</dialog>>
<<dialog "dinerClerk">>She's good at rebranding my bad habits.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Tom holds his coffee close, eyes half-closed in the quiet of the back room.<</narrative>>
<<dialog "dinerClerk">>Five more minutes and I'm back out there.<</dialog>>
<<dialog "player">>Counting down already?<</dialog>>
<<dialog "dinerClerk">>Always. Breaks go fast when you need them.<</dialog>>
<<dialog "player">>That's how it works, unfortunately.<</dialog>>
<<dialog "dinerClerk">>Universal law of break rooms.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"normal_life_stuff": [
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-onBreak-4.webp">>
<<image _img "100%">>
<<narrative>>Tom scrolls through his phone, then sets it face-down on his knee.<</narrative>>
<<dialog "dinerClerk">>My apartment's AC broke again last night.<</dialog>>
<<dialog "player">>In this heat? That's rough.<</dialog>>
<<dialog "dinerClerk">>Landlord says Thursday. I'm counting on it.<</dialog>>
<<dialog "player">>Fingers crossed for you.<</dialog>>
<<dialog "dinerClerk">>Might sleep at the diner if it doesn't happen.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-onBreak-5.webp">>
<<image _img "100%">>
<<narrative>>Tom picks at the label on his coffee cup, peeling it in a long strip.<</narrative>>
<<dialog "dinerClerk">>Need to get my car inspected. Keep putting it off.<</dialog>>
<<dialog "player">>Those things sneak up on you.<</dialog>>
<<dialog "dinerClerk">>It expired two months ago. I'm living dangerously.<</dialog>>
<<dialog "player">>That's one way to put it.<</dialog>>
<<dialog "dinerClerk">>Girlfriend's gonna kill me if I get a ticket.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/tom/level1/tomStorageAfternoon-onBreak-6.webp">>
<<image _img "100%">>
<<narrative>>Tom yawns, covering his mouth with the back of his hand.<</narrative>>
<<dialog "dinerClerk">>Stayed up too late watching a show. Bad idea.<</dialog>>
<<dialog "player">>What show?<</dialog>>
<<dialog "dinerClerk">>Some crime thing. Got hooked and couldn't stop.<</dialog>>
<<dialog "player">>Been there. One more episode turns into five.<</dialog>>
<<dialog "dinerClerk">>Every single time.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<set setup.vinceTopics = setup.vinceTopics || {}>>
<<set setup.vinceTopics.common = setup.vinceTopics.common || {}>>
<<set setup.vinceTopics.common.level1 = {
"dinerRubys_common_afternoon": {
"the_floor": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-common-1.webp">>
<<image _img "100%">>
<<narrative>>Vince straightens a chair that was already straight, then turns to you with a smile that arrives a half-second too fast.<</narrative>>
<<dialog "dinerManager">>Well. Haven't seen you in a while.<</dialog>>
<<dialog "player">>I was here last week, actually.<</dialog>>
<<dialog "dinerManager">>Was you? Time flies around here.<</dialog>>
<<dialog "player">>Place looks good though.<</dialog>>
<<dialog "dinerManager">>I try. Details matter, you know?<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-common-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is adjusting a table centerpiece when he notices you. He steps closer than expected to greet you.<</narrative>>
<<dialog "dinerManager">>There she is. Afternoon visit?<</dialog>>
<<dialog "player">>Just stopping by for a bit.<</dialog>>
<<dialog "dinerManager">>Good. Place is better with company.<</dialog>>
<<dialog "player">>You say that to everyone?<</dialog>>
<<dialog "dinerManager">>Only the ones worth saying it to.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-common-3.webp">>
<<image _img "100%">>
<<narrative>>Vince stands near the front window, surveying the dining room with his arms loosely crossed. He watches you walk in, holding eye contact a beat too long before speaking.<</narrative>>
<<dialog "dinerManager">>Afternoon's the best time to come in.<</dialog>>
<<dialog "player">>Less crowded, I guess.<</dialog>>
<<dialog "dinerManager">>Exactly. I can actually talk to people.<</dialog>>
<<dialog "player">>Sounds like you prefer it quiet.<</dialog>>
<<dialog "dinerManager">>I prefer quality over quantity.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"business_talk": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-common-4.webp">>
<<image _img "100%">>
<<narrative>>Vince runs his hand along the counter as he walks, checking for dust. He finds none but frowns anyway.<</narrative>>
<<dialog "dinerManager">>Thinking about expanding the menu. Modernizing.<</dialog>>
<<dialog "player">>What kind of changes?<</dialog>>
<<dialog "dinerManager">>Nothing crazy. Just... elevating things a bit.<</dialog>>
<<dialog "player">>The regulars might not love that.<</dialog>>
<<dialog "dinerManager">>Change is good. People just need someone to guide them.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-common-5.webp">>
<<image _img "100%">>
<<narrative>>Vince leans on the register counter, glancing around the room like he's appraising it. He turns his attention to you fully, head slightly tilted.<</narrative>>
<<dialog "dinerManager">>You ever think about the restaurant business?<</dialog>>
<<dialog "player">>Not really. Seems like a lot of work.<</dialog>>
<<dialog "dinerManager">>It is. But it's mine, so that makes it different.<</dialog>>
<<dialog "player">>Yeah, I get that.<</dialog>>
<<dialog "dinerManager">>Ownership changes everything. Remember that.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-common-6.webp">>
<<image _img "100%">>
<<narrative>>Vince adjusts the specials board, erasing a price and rewriting the same number. He glances over his shoulder at you.<</narrative>>
<<dialog "dinerManager">>Numbers are up this quarter. Not bragging.<</dialog>>
<<dialog "player">>Sounds like bragging a little.<</dialog>>
<<dialog "dinerManager">>Just stating facts. I like facts.<</dialog>>
<<dialog "player">>Fair enough. The place does seem busy.<</dialog>>
<<dialog "dinerManager">>Busy because I run it right.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubys_common_evening": {
"evening_crowd": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-common-1.webp">>
<<image _img "100%">>
<<narrative>>Vince moves between tables during the dinner rush, touching the back of an empty chair here, adjusting a salt shaker there. He spots you and closes the distance with a deliberate stride.<</narrative>>
<<dialog "dinerManager">>Evening. Dining in tonight?<</dialog>>
<<dialog "player">>Maybe. Still deciding.<</dialog>>
<<dialog "dinerManager">>Take your time. I'll save you a good spot.<</dialog>>
<<dialog "player">>You don't have to do that.<</dialog>>
<<dialog "dinerManager">>I know I don't have to.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-common-2.webp">>
<<image _img "100%">>
<<narrative>>Vince stands near the door, greeting customers as they come in. His smile looks practiced but effective. He gives you a slow nod as you enter.<</narrative>>
<<dialog "dinerManager">>The evening crowd keeps this place alive.<</dialog>>
<<dialog "player">>Looks like a good turnout tonight.<</dialog>>
<<dialog "dinerManager">>Every night's a good night if you work it right.<</dialog>>
<<dialog "player">>Positive attitude.<</dialog>>
<<dialog "dinerManager">>Not attitude. Strategy.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-common-3.webp">>
<<image _img "100%">>
<<narrative>>Vince straightens his collar near the front, scanning the room. When he sees you, he steps over and stands just inside your personal space, hands in his pockets.<</narrative>>
<<dialog "dinerManager">>You picked a good night to visit.<</dialog>>
<<dialog "player">>What makes tonight special?<</dialog>>
<<dialog "dinerManager">>Nothing. I'm just in a good mood.<</dialog>>
<<dialog "player">>That rare for you?<</dialog>>
<<dialog "dinerManager">>I'm always in a good mood. Some days are just better.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"appearance_comment": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-common-4.webp">>
<<image _img "100%">>
<<narrative>>Vince leans against the register counter, coffee in hand. His eyes track you as you approach, lingering a moment before he speaks.<</narrative>>
<<dialog "dinerManager">>You look different today. New haircut?<</dialog>>
<<dialog "player">>No, same as always.<</dialog>>
<<dialog "dinerManager">>Huh. Must just be the lighting then.<</dialog>>
<<dialog "player">>Sure. Must be the lighting.<</dialog>>
<<dialog "dinerManager">>Either way. Looks good.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-common-5.webp">>
<<image _img "100%">>
<<narrative>>Vince tucks a pen into his breast pocket, watching you from the register. He holds eye contact just long enough for it to feel deliberate.<</narrative>>
<<dialog "dinerManager">>Long day? You look a little tired.<</dialog>>
<<dialog "player">>Thanks for that, I guess.<</dialog>>
<<dialog "dinerManager">>I meant it as concern. Not criticism.<</dialog>>
<<dialog "player">>Hard to tell with you sometimes.<</dialog>>
<<dialog "dinerManager">>That's fair. I'll work on that.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-common-6.webp">>
<<image _img "100%">>
<<narrative>>Vince walks past, then pauses and turns back. He gives you a once-over that's quick but thorough, like he's taking inventory.<</narrative>>
<<dialog "dinerManager">>Heading somewhere after this?<</dialog>>
<<dialog "player">>No, just came here. Why?<</dialog>>
<<dialog "dinerManager">>You look put together. That's all.<</dialog>>
<<dialog "player">>I just threw this on, honestly.<</dialog>>
<<dialog "dinerManager">>Some people make that look easy.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysManagerOffice_common_morning": {
"paperwork_morning": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-common-1.webp">>
<<image _img "100%">>
<<narrative>>Vince sits behind his desk, glasses on, staring at a spreadsheet. He looks up when you knock, and for a second he looks genuinely tired before the smile clicks into place.<</narrative>>
<<dialog "dinerManager">>Morning. Come in, come in.<</dialog>>
<<dialog "player">>Sorry, didn't mean to interrupt.<</dialog>>
<<dialog "dinerManager">>You're not. Just numbers. They'll wait.<</dialog>>
<<dialog "player">>Looks like a lot of paperwork.<</dialog>>
<<dialog "dinerManager">>Nobody tells you this part when you buy a restaurant.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-common-2.webp">>
<<image _img "100%">>
<<narrative>>Vince leans back in his chair, rubbing his eyes. The desk is covered in receipts and invoices. He drops his hands and straightens up when he sees you.<</narrative>>
<<dialog "dinerManager">>Mornings are for the boring stuff.<</dialog>>
<<dialog "player">>Tax stuff or supply orders?<</dialog>>
<<dialog "dinerManager">>Both. Plus payroll. Plus inspections.<</dialog>>
<<dialog "player">>Sounds like it never stops.<</dialog>>
<<dialog "dinerManager">>It doesn't. That's the job.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-common-3.webp">>
<<image _img "100%">>
<<narrative>>Vince has his phone to his ear but lowers it when you appear in the doorway. The call was clearly already over. He gestures you in.<</narrative>>
<<dialog "dinerManager">>Supplier's late again. Typical.<</dialog>>
<<dialog "player">>Does that happen a lot?<</dialog>>
<<dialog "dinerManager">>More than it should. I'm handling it.<</dialog>>
<<dialog "player">>You seem pretty on top of things.<</dialog>>
<<dialog "dinerManager">>I have to be. Nobody else will be.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"divorce_veiled": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-common-4.webp">>
<<image _img "100%">>
<<narrative>>Vince stares at a framed photo on his desk, then turns it face-down before you can see it. He looks up with a thin smile.<</narrative>>
<<dialog "dinerManager">>Fresh start. That's what mornings are for.<</dialog>>
<<dialog "player">>You sound like a motivational poster.<</dialog>>
<<dialog "dinerManager">>Maybe. But I mean it lately.<</dialog>>
<<dialog "player">>Something going on?<</dialog>>
<<dialog "dinerManager">>Just... restructuring. Life stuff. Nothing major.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-common-5.webp">>
<<image _img "100%">>
<<narrative>>Vince is staring out the small office window, coffee untouched on his desk. He turns when you come in, and the tiredness doesn't quite leave his eyes.<</narrative>>
<<dialog "dinerManager">>You know what I like about this place?<</dialog>>
<<dialog "player">>The food?<</dialog>>
<<dialog "dinerManager">>It's predictable. Same thing every day. That's comforting.<</dialog>>
<<dialog "player">>I guess that makes sense.<</dialog>>
<<dialog "dinerManager">>Not everything in life is that reliable.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-common-6.webp">>
<<image _img "100%">>
<<narrative>>Vince drums his fingers on the desk, jaw tight. He relaxes it deliberately when he notices you, switching to a casual lean.<</narrative>>
<<dialog "dinerManager">>Sold the house last month. Downsizing.<</dialog>>
<<dialog "player">>That's a big change.<</dialog>>
<<dialog "dinerManager">>Apartment's fine. Less to maintain.<</dialog>>
<<dialog "player">>Silver lining, I guess.<</dialog>>
<<dialog "dinerManager">>Exactly. I'm good at finding those.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
},
"dinerRubysManagerOffice_common_afternoon": {
"expansion_plans": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-common-1.webp">>
<<image _img "100%">>
<<narrative>>Vince has blueprints spread across his desk, weighed down with coffee mugs. He waves you over without looking up, pointing at something on the paper.<</narrative>>
<<dialog "dinerManager">>Thinking about a second location. What do you think?<</dialog>>
<<dialog "player">>That's ambitious. Where would it be?<</dialog>>
<<dialog "dinerManager">>Uptown. Different crowd, bigger margins.<</dialog>>
<<dialog "player">>Sounds like a lot of work on top of this.<</dialog>>
<<dialog "dinerManager">>I don't mind work. I mind standing still.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-common-2.webp">>
<<image _img "100%">>
<<narrative>>Vince circles something on a printed spreadsheet, then leans back, tapping the pen against his lip. He looks at you like he just remembered you were there.<</narrative>>
<<dialog "dinerManager">>Five-year plan. Everything mapped out.<</dialog>>
<<dialog "player">>You have a five-year plan for the diner?<</dialog>>
<<dialog "dinerManager">>For the brand. The diner's just phase one.<</dialog>>
<<dialog "player">>Phase one. Okay.<</dialog>>
<<dialog "dinerManager">>You sound skeptical. I like that. Keeps me sharp.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-common-3.webp">>
<<image _img "100%">>
<<narrative>>Vince stands by a small corkboard with photos, clippings, and a few business cards pinned to it. He pulls one off and hands it to you, standing close enough that you can smell his cologne.<</narrative>>
<<dialog "dinerManager">>Know any good contractors? I'm renovating the back.<</dialog>>
<<dialog "player">>Not off the top of my head, sorry.<</dialog>>
<<dialog "dinerManager">>No worries. I'll figure it out. I always do.<</dialog>>
<<dialog "player">>I'm sure you will.<</dialog>>
<<dialog "dinerManager">>Appreciate the confidence, sweetheart.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
],
"personal_questions": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-common-4.webp">>
<<image _img "100%">>
<<narrative>>Vince is sorting through mail on his desk. He sets it aside when you come in, giving you his full attention. It feels like a lot of attention.<</narrative>>
<<dialog "dinerManager">>So what do you do with your afternoons?<</dialog>>
<<dialog "player">>Not much today. Just wandering around.<</dialog>>
<<dialog "dinerManager">>Wandering. I used to do that. Before all this.<</dialog>>
<<dialog "player">>You miss it?<</dialog>>
<<dialog "dinerManager">>Sometimes. But I traded up.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-common-5.webp">>
<<image _img "100%">>
<<narrative>>Vince closes a laptop screen as you walk in, a little too quickly. He folds his hands on the desk and smiles.<</narrative>>
<<dialog "dinerManager">>You live nearby, right?<</dialog>>
<<dialog "player">>Yeah, not far. Why?<</dialog>>
<<dialog "dinerManager">>Just curious. I notice regulars. Part of the job.<</dialog>>
<<dialog "player">>I'm not sure I'm a regular yet.<</dialog>>
<<dialog "dinerManager">>You're getting there. Don't worry.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-common-6.webp">>
<<image _img "100%">>
<<narrative>>Vince leans forward across the desk, chin resting on his knuckles. He studies your face like he's reading something interesting.<</narrative>>
<<dialog "dinerManager">>You always come in alone?<</dialog>>
<<dialog "player">>Usually. Is that weird?<</dialog>>
<<dialog "dinerManager">>Not weird. Independent. I respect that.<</dialog>>
<<dialog "player">>It's not really a statement or anything.<</dialog>>
<<dialog "dinerManager">>Everything's a statement. Most people just don't realize it.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
"dinerRubysManagerOffice_common_evening": {
"end_of_day": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-common-1.webp">>
<<image _img "100%">>
<<narrative>>Vince sits back in his chair, tie loosened, staring at the ceiling. The office is dim just the desk lamp on. He looks like he forgot someone might walk in.<</narrative>>
<<dialog "dinerManager">>Long day. Longer night ahead.<</dialog>>
<<dialog "player">>You're here late.<</dialog>>
<<dialog "dinerManager">>Where else would I be?<</dialog>>
<<dialog "player">>Home, maybe?<</dialog>>
<<dialog "dinerManager">>Home's just a place to sleep. This is where I live.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-common-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is closing up binders, stacking them in a neat pile. He moves slower than usual. The performance is at half-power this late.<</narrative>>
<<dialog "dinerManager">>Evenings make me philosophical. Ignore it.<</dialog>>
<<dialog "player">>That's a weird warning to give.<</dialog>>
<<dialog "dinerManager">>Just don't want to bore you.<</dialog>>
<<dialog "player">>I'll risk it.<</dialog>>
<<dialog "dinerManager">>Brave. I like that about you.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-common-3.webp">>
<<image _img "100%">>
<<narrative>>Vince pours himself a glass of something from a bottle in his desk drawer. He pauses, then holds the bottle toward you an offer that feels like it carries weight.<</narrative>>
<<dialog "dinerManager">>Nightcap? Don't tell the health inspector.<</dialog>>
<<dialog "player">>I'm good, but thanks.<</dialog>>
<<dialog "dinerManager">>Smart. One of us should be responsible.<</dialog>>
<<dialog "player">>Long day?<</dialog>>
<<dialog "dinerManager">>They're all long. Some are just louder.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
"winding_down": [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-common-4.webp">>
<<image _img "100%">>
<<narrative>>Vince stands by the office window, watching the dining room through the glass. The overhead light is off; just the glow from outside. He doesn't turn around right away.<</narrative>>
<<dialog "dinerManager">>I watch them sometimes. From up here.<</dialog>>
<<dialog "player">>The customers?<</dialog>>
<<dialog "dinerManager">>The staff. Making sure things run smooth.<</dialog>>
<<dialog "player">>That doesn't sound relaxing.<</dialog>>
<<dialog "dinerManager">>It's not supposed to be.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-common-5.webp">>
<<image _img "100%">>
<<narrative>>Vince has his jacket over the back of his chair, sleeves rolled up. He looks less polished than usual. Almost normal.<</narrative>>
<<dialog "dinerManager">>You ever get tired of people?<</dialog>>
<<dialog "player">>Sometimes. Depends on the people.<</dialog>>
<<dialog "dinerManager">>Fair answer. Most people lie about that.<</dialog>>
<<dialog "player">>You asking because you're tired of them?<</dialog>>
<<dialog "dinerManager">>I run a diner. I can't afford to be.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-common-6.webp">>
<<image _img "100%">>
<<narrative>>Vince leans on the edge of his desk, arms folded. The usual sharpness in his eyes has softened to something duller. He looks at you without the usual evaluation.<</narrative>>
<<dialog "dinerManager">>Thanks for stopping by. Means something.<</dialog>>
<<dialog "player">>Don't get many visitors up here?<</dialog>>
<<dialog "dinerManager">>People come when they need something. You didn't.<</dialog>>
<<dialog "player">>Just checking in. That's all.<</dialog>>
<<dialog "dinerManager">>That's more than most.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
}
]
}
}>>
<</nobr>>/* ==========================================
VINCE TALK TOPICS WORKING DONE, LEVEL 1
phase === "dishwasherDone" && friendshipLevel === 1
CHARACTER: Vince, Manager (dinerManager)
→ Tired, reflective, showing the "real" Vince at the end of a shift.
→ Less performance, more vulnerability (in his way).
========================================== */
<<nobr>>
<<set setup.vinceTopics = setup.vinceTopics || {}>>
<<set setup.vinceTopics.dishwasherDone = setup.vinceTopics.dishwasherDone || {}>>
<<set setup.vinceTopics.dishwasherDone.level1 = {
dinerRubysManagerOffice_dishwasherDone_morning: {
office_morning_done: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-done-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is staring at a cold cup of coffee, his hand resting on the desk but not moving. He looks like he's been in this position for a while.<</narrative>>
<<dialog "dinerManager">>Morning's over. But the day... it just hangs there.<</dialog>>
<<dialog "player">>You should go home and get some real rest, Vince.<</dialog>>
<<dialog "dinerManager">>Rest is for those who finished their list. I only added to mine.<</dialog>>
<<dialog "player">>The list will be there tomorrow.<</dialog>>
<<dialog "dinerManager">>That's exactly what I'm afraid of.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-done-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is slowly shredding a piece of paper by hand, watching the strips fall into the wastebasket.<</narrative>>
<<dialog "dinerManager">>Some things are better off as confetti.<</dialog>>
<<dialog "player">>Mistakes on those papers?<</dialog>>
<<dialog "dinerManager">>History. Ancient history from two hours ago.<</dialog>>
<<dialog "player">>You're very dramatic about clearing your desk.<</dialog>>
<<dialog "dinerManager">>Clearing the desk is clearing the mind. I'm nearly there.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-done-3.webp">>
<<image _img "100%">>
<<narrative>>Vince has his forehead resting against the cool surface of his desk. He doesn't move when you knock.<</narrative>>
<<dialog "dinerManager">>Speak quietly. The walls have ears, and they're loud today.<</dialog>>
<<dialog "player">>Just finished my shift. Heading out.<</dialog>>
<<dialog "dinerManager">>Go. Enjoy the sun before it starts judging you.<</dialog>>
<<dialog "player">>Sun doesn't judge, Vince. That's just you.<</dialog>>
<<dialog "dinerManager">>Maybe. But I'm usually right.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubysManagerOffice_dishwasherDone_afternoon: {
office_afternoon_done: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-done-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is looking at a ledger book, his finger tracing a line of red ink. He doesn't look up when you speak.<</narrative>>
<<dialog "dinerManager">>Red ink doesn't lie. It just hurts.<</dialog>>
<<dialog "player">>Is it that bad? The business, I mean.<</dialog>>
<<dialog "dinerManager">>It's not bad. It's just... demanding. It asks for everything.<</dialog>>
<<dialog "player">>You give it everything, Vince. Take a break.<</dialog>>
<<dialog "dinerManager">>If I stop, everything stops. You'll understand one day.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-done-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is organizeing a drawer full of old business cards, throwing most of them away.<</narrative>>
<<dialog "dinerManager">>Contacts. Bridges that went nowhere.<</dialog>>
<<dialog "player">>You sound a bit cynical about your network.<</dialog>>
<<dialog "dinerManager">>I'm realistic. Most people are just noise in the background.<</dialog>>
<<dialog "player">>I hope I'm not just noise.<</dialog>>
<<dialog "dinerManager">>You're a very distinct signal, $player.firstName. Hard to ignore.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-done-3.webp">>
<<image _img "100%">>
<<narrative>>Vince is sitting at his desk, but his chair is turned toward the window, watching a bird on the ledge.<</narrative>>
<<dialog "dinerManager">>Look at that. No bills, no payroll, no Emma complaining.<</dialog>>
<<dialog "player">>Simple life. But not much of a future for a bird.<</dialog>>
<<dialog "dinerManager">>A future is just a fancy way of saying more problems.<</dialog>>
<<dialog "player">>You're in a mood today, Vince.<</dialog>>
<<dialog "dinerManager">>It's the afternoon lull. It makes me introspective. Go home.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubysManagerOffice_dishwasherDone_evening: {
the_last_light: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-done-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is standing by the window, looking out at the darkened parking lot. The only light in the office is the green glow from the exit sign.<</narrative>>
<<dialog "dinerManager">>The end of the day. The most honest hour.<</dialog>>
<<dialog "player">>Looks like you're ready to head home too.<</dialog>>
<<dialog "dinerManager">>Home's... quiet. I prefer the hum of the diner.<</dialog>>
<<dialog "player">>Even when it's empty?<</dialog>>
<<dialog "dinerManager">>Especially then. It feels like it's all mine.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-done-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is methodically packing his briefcase. His movements are slower than they were during the lunch rush.<</narrative>>
<<dialog "dinerManager">>I used to have someone waiting for me at home.<</dialog>>
<<dialog "player">>Is that a recent change?<</dialog>>
<<dialog "dinerManager">>Recent enough. Restructuring is never easy.<</dialog>>
<<dialog "player">>Sorry to hear that, Vince.<</dialog>>
<<dialog "dinerManager">>Don't be. Efficiency requires sacrifice.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-done-3.webp">>
<<image _img "100%">>
<<narrative>>Vince sits on the edge of his desk, staring at the empty chairs in the dining room below.<</narrative>>
<<dialog "dinerManager">>Do you ever wonder if you're building something or just filling time?<</dialog>>
<<dialog "player">>I try not to think about it that much.<</dialog>>
<<dialog "dinerManager">>Wise. Thinking is the enemy of productivity.<</dialog>>
<<dialog "player">>You seem to do a lot of it though.<</dialog>>
<<dialog "dinerManager">>It's a manager's burden. Enjoy your freedom, $player.firstName.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
],
vince_lonely: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-done-4.webp">>
<<image _img "100%">>
<<narrative>>Vince is holding an empty glass, staring at the condensation pattern on his desk. He looks up, and for once, the smile doesn't arrive.<</narrative>>
<<dialog "dinerManager">>Do you think people stay because they want to, or because they have to?<</dialog>>
<<dialog "player">>I stay because I like it here. Mostly.<</dialog>>
<<dialog "dinerManager">>Mostly. That's a very honest word, $player.firstName. I like it.<</dialog>>
<<dialog "player">>Honesty is free. You should try it more.<</dialog>>
<<dialog "dinerManager">>I'm trying. It's... a new experience for me.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-done-5.webp">>
<<image _img "100%">>
<<narrative>>Vince is standing with his forehead against the cool glass of the window, eyes closed.<</narrative>>
<<dialog "dinerManager">>Sometimes I forget what it's like to just be... a person.<</dialog>>
<<dialog "player">>The manager's mask is heavy today?<</dialog>>
<<dialog "dinerManager">>Heavier than usual. It's a gold-plated burden.<</dialog>>
<<dialog "player">>You're the one who chose the plating, Vince.<</dialog>>
<<dialog "dinerManager">>I did. And I'd probably do it again. But tonight... it's just heavy.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-done-6.webp">>
<<image _img "100%">>
<<narrative>>Vince is sitting in his chair, rotated away from the desk, looking at a small, old keychain in his hand. He pockets it quickly when he sees you.<</narrative>>
<<dialog "dinerManager">>Memories are bad for business. They make you look back.<</dialog>>
<<dialog "player">>Everyone looks back sometimes, Vince.<</dialog>>
<<dialog "dinerManager">>I prefer looking forward. But the past has a way of catching up in the dark.<</dialog>>
<<dialog "player">>Maybe it just wants to be acknowledged.<</dialog>>
<<dialog "dinerManager">>Acknowledgement is expensive. I'm not sure I'm ready to pay yet.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
},
dinerRubys_dishwasherDone_afternoon: {
front_done_afternoon: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-done-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is wiping down the pastry case, his movements slow and deliberate. He watches you walk toward the exit.<</narrative>>
<<dialog "dinerManager">>The day's half gone. And you're leaving me with the rest.<</dialog>>
<<dialog "player">>Shift's over, Vince. I'm off the clock.<</dialog>>
<<dialog "dinerManager">>The clock is just a suggestion. But I'll let it slide today.<</dialog>>
<<dialog "player">>How kind of you. See you tomorrow.<</dialog>>
<<dialog "dinerManager">>Try to be early. I have big plans for the morning.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-done-2.webp">>
<<image _img "100%">>
<<narrative>>Vince stands by the host stand, checking the reservations list. He looks up as you approach the door, standing in your path just long enough to be noticed.<</narrative>>
<<dialog "dinerManager">>Another one bites the dust. Heading for the freedom of the street?<</dialog>>
<<dialog "player">>Just heading home, Vince.<</dialog>>
<<dialog "dinerManager">>Home is where the work you can't delegate waits for you.<</dialog>>
<<dialog "player">>You make home sound like a second job.<</dialog>>
<<dialog "dinerManager">>If you're doing it right, it is.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-done-3.webp">>
<<image _img "100%">>
<<narrative>>Vince is adjustting the 'Open' sign, making sure it's perfectly level. He glances at you with a slight, tired smirk.<</narrative>>
<<dialog "dinerManager">>You look lighter. It's almost offensive.<</dialog>>
<<dialog "player">>That's the feeling of not having to wash any more dishes.<</dialog>>
<<dialog "dinerManager">>Enjoy it while it lasts. The sink is a jealous mistress, $player.firstName.<</dialog>>
<<dialog "player">>I think I can handle her absence for a few hours.<</dialog>>
<<dialog "dinerManager">>Go then. Before I find a reason for you to stay.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubys_dishwasherDone_evening: {
front_done_evening: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-done-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is standing near the entrance, looking at the street lights through the glass. He looks unusually still. He doesn't turn when you walk up behind him.<</narrative>>
<<dialog "dinerManager">>They look like stars. If you squint and lie to yourself.<</dialog>>
<<dialog "player">>Streetlights? They're just orange lights, Vince.<</dialog>>
<<dialog "dinerManager">>Aesthetic is all about the lie you tell. Don't ruin mine.<</dialog>>
<<dialog "player">>Sorry. They're beautiful stars, Vince.<</dialog>>
<<dialog "dinerManager">>Too late. The streetlights are back. Go home, $player.firstName.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-done-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is sitting at a booth, tie completely removed and hanging over the back of the seat. He's staring at an empty plate.<</narrative>>
<<dialog "dinerManager">>I forgot to eat. Again.<</dialog>>
<<dialog "player">>You run a restaurant, Vince. That shouldn't be possible.<</dialog>>
<<dialog "dinerManager">>The shoemaker's children go barefoot. Same principle.<</dialog>>
<<dialog "player">>Go in the back and have James whip you something up.<</dialog>>
<<dialog "dinerManager">>James is tired. I'll just have a protein bar when I get home.<</dialog>>`,
friendship: 2, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-done-3.webp">>
<<image _img "100%">>
<<narrative>>Vince is checking the register tape for the final time, the paper curling around his hand. He looks at you through the dim light of the dining room.<</narrative>>
<<dialog "dinerManager">>Another day in the ledger. Every one counts.<</dialog>>
<<dialog "player">>You're always focused on the counts, aren't you?<</dialog>>
<<dialog "dinerManager">>If you don't count, you don't matter. It's simple math.<</dialog>>
<<dialog "player">>Mathematics doesn't account for people though.<</dialog>>
<<dialog "dinerManager">>On the contrary, people are the most difficult variables of all.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
}
]
},
dinerRubysKitchen_dishwasherDone_afternoon: {
kitchen_done_afternoon: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceKitchenAfternoon-done-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is standing by the dishwasher, poking at a rubber seal with a look of extreme frustration.<</narrative>>
<<dialog "dinerManager">>This machine is a traitor. It's planning a rebellion.<</dialog>>
<<dialog "player">>It's just old, Vince. Needs a new belt.<</dialog>>
<<dialog "dinerManager">>I don't accept aging as an excuse. Not for machines, not for people.<</dialog>>
<<dialog "player">>Tell that to my sore back.<</dialog>>
<<dialog "dinerManager">>Your back is an investment. Maintenance is required. Go home, $player.firstName.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceKitchenAfternoon-done-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is leaning against the stainless steel prep table, watching the last of the afternoon steam clear out. He looks small in the large kitchen.<</narrative>>
<<dialog "dinerManager">>It's so quiet when James isn't shouting.<</dialog>>
<<dialog "player">>It's a nice change, isn't it?<</dialog>>
<<dialog "dinerManager">>It's eerie. I prefer the noise. It means money is being made.<</dialog>>
<<dialog "player">>You only think about the money.<</dialog>>
<<dialog "dinerManager">>Money is the only honest way to measure progress.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceKitchenAfternoon-done-3.webp">>
<<image _img "100%">>
<<narrative>>Vince is checking the fridge temperatures, writing numbers on a clipboard with a hand that looks a little shaky.<</narrative>>
<<dialog "dinerManager">>Consistency. That's the only thing that saves us from chaos.<</dialog>>
<<dialog "player">>You've been checking those every ten minutes.<</dialog>>
<<dialog "dinerManager">>And they've been consistent every ten minutes. It's beautiful.<</dialog>>
<<dialog "player">>It's obsessive, Vince.<</dialog>>
<<dialog "dinerManager">>Obsession is just what lazy people call commitment.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>>/* ==========================================
VINCE TALK TOPICS WORKING ON BREAK, LEVEL 1
phase === "dishwasherOnBreak" && friendshipLevel === 1
CHARACTER: Vince, Manager (dinerManager)
→ Performative, subtly controlling, subtly creepy.
→ Office: More honest, tired, vulnerable.
→ Front: Acting as the owner, authority.
→ Kitchen: Intruding, staff tenses up.
========================================== */
<<nobr>>
<<set setup.vinceTopics = setup.vinceTopics || {}>>
<<set setup.vinceTopics.dishwasherOnBreak = setup.vinceTopics.dishwasherOnBreak || {}>>
<<set setup.vinceTopics.dishwasherOnBreak.level1 = {
dinerRubysManagerOffice_dishwasherOnBreak_morning: {
office_morning_break: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is leaning back in his chair with a cold compress over his eyes. He doesn't move it when you knock.<</narrative>>
<<dialog "dinerManager">>The light is particularly aggressive this morning, don't you think?<</dialog>>
<<dialog "player">>Usually helps to open a window, Vince.<</dialog>>
<<dialog "dinerManager">>The window stays shut. I prefer to control the climate in here.<</dialog>>
<<dialog "player">>Fair enough. Headache?<</dialog>>
<<dialog "dinerManager">>Clarity. Pain is just a byproduct of thinking too hard.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is meticulously cleaning the lens of his webcam with a specialized cloth. He looks focused.<</narrative>>
<<dialog "dinerManager">>First impressions are digital now. Did you know that?<</dialog>>
<<dialog "player">>I thought your first impressions were made on the floor.<</dialog>>
<<dialog "dinerManager">>The floor is for the regulars. The camera is for the investors.<</dialog>>
<<dialog "player">>You getting a lot of calls lately?<</dialog>>
<<dialog "dinerManager">>Enough to keep me... monitored. Sit. Tell me about your morning.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeMorning-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Vince is staring at a small digital clock on his desk, watching the seconds tick by. He's completely still.<</narrative>>
<<dialog "dinerManager">>Time is the only thing we can't buy more of, $player.firstName.<</dialog>>
<<dialog "player">>Is that why you're watching it so closely?<</dialog>>
<<dialog "dinerManager">>I'm making sure it's not cheating me. Every second has to pay its way.<</dialog>>
<<dialog "player">>That sounds like a stressful way to live.<</dialog>>
<<dialog "dinerManager">>It's expensive. Stress is the currency of the successful.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubysManagerOffice_dishwasherOnBreak_afternoon: {
behind_the_curtain: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Vince has his feet up on the desk, tie loosened. He's staring at the ceiling when you knock.<</narrative>>
<<dialog "dinerManager">>Come in. I was just... visualizing success.<</dialog>>
<<dialog "player">>Looks more like a nap to me.<</dialog>>
<<dialog "dinerManager">>Vision requires rest. And quiet.<</dialog>>
<<dialog "player">>I can leave if you want quiet.<</dialog>>
<<dialog "dinerManager">>No. Stay. Your energy is... refreshing.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is rotating a small desktop globe slowly, his eyes following continents he'll probably never visit.<</narrative>>
<<dialog "dinerManager">>The world is a very small place, $player.firstName. If you own enough of it.<</dialog>>
<<dialog "player">>You planning on buying a continent now?<</dialog>>
<<dialog "dinerManager">>Just starting with this block. One storefront at a time.<</dialog>>
<<dialog "player">>Don't forget to pay your staff while you're at it.<</dialog>>
<<dialog "dinerManager">>A well-fed army is a loyal one. I haven't forgotten.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Vince is cleaning his glasses with a silk cloth, looking down at his desk. He doesn't look as sharp as he does on the floor.<</narrative>>
<<dialog "dinerManager">>People think this job is just shaking hands.<</dialog>>
<<dialog "player">>I know there's more to it than that.<</dialog>>
<<dialog "dinerManager">>It's managing personalities. Which is exhausting.<</dialog>>
<<dialog "player">>You seem to handle it well enough.<</dialog>>
<<dialog "dinerManager">>I have a very high tolerance for drama. Usually.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
],
personal_questions_office: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-onBreak-4.webp">>
<<image _img "100%">>
<<narrative>>Vince is sipping on a bottle of high-end mineral water, looking at you like you're a puzzle he's almost solved.<</narrative>>
<<dialog "dinerManager">>What's your biggest ambition, $player.firstName? The real one.<</dialog>>
<<dialog "player">>I'm still figuring that out, honestly.<</dialog>>
<<dialog "dinerManager">>Ambition isn't found. It's built. I could help you with the blueprints.<</dialog>>
<<dialog "player">>I'm not sure I'm ready for a career coach.<</dialog>>
<<dialog "dinerManager">>It was an offer, not an obligation. Think about it.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-onBreak-5.webp">>
<<image _img "100%">>
<<narrative>>Vince is idly tossing a stress ball up and down, catch it with practiced precision.<</narrative>>
<<dialog "dinerManager">>You ever think about what makes people stay in one place?<</dialog>>
<<dialog "player">>Comfort? Family? Lack of options?<</dialog>>
<<dialog "dinerManager">>Fear. Most people are just afraid of the unknown.<</dialog>>
<<dialog "player">>You don't seem like the fearful type, Vince.<</dialog>>
<<dialog "dinerManager">>I embrace the unknown. I find it profitable.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeAfternoon-onBreak-6.webp">>
<<image _img "100%">>
<<narrative>>Vince leans forward, closing a personal-looking folder. He watches you with a half-smile.<</narrative>>
<<dialog "dinerManager">>Do you ever feel like you're meant for more than this town?<</dialog>>
<<dialog "player">>Sometimes. Doesn't everyone?<</dialog>>
<<dialog "dinerManager">>Not like I do. I see potential in you, $player.firstName.<</dialog>>
<<dialog "player">>You've said that before, Vince.<</dialog>>
<<dialog "dinerManager">>Because it's true. I have an eye for these things.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubysManagerOffice_dishwasherOnBreak_evening: {
vince_restructured_office: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is staring at a blank space on the wall where a picture used to hang. He looks unusually vulnerable for a brief moment.<</narrative>>
<<dialog "dinerManager">>Silence is very loud sometimes. Don't you think?<</dialog>>
<<dialog "player">>Is everything okay overall, Vince?<</dialog>>
<<dialog "dinerManager">>Efficiency requires cutting ties. Even the ones you thought were permanent.<</dialog>>
<<dialog "player">>Sounds like an expensive lesson.<</dialog>>
<<dialog "dinerManager">>The most important ones always are. Come, let's look at these projections.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is holding a framed photo on his desk, but it's face down. He doesn't move it when you come in.<</narrative>>
<<dialog "dinerManager">>I used to have everything mapped out. A different map.<</dialog>>
<<dialog "player">>Life has a way of changing the terrain.<</dialog>>
<<dialog "dinerManager">>That it does. But I'm good at drawing new lines.<</dialog>>
<<dialog "player">>Doesn't make the old ones stay gone though.<</dialog>>
<<dialog "dinerManager">>They fade. Eventually. If you're busy enough.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceOfficeEvening-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Vince stands by his small office liquor cabinet, but he's only holding a glass of club soda.<</narrative>>
<<dialog "dinerManager">>Sobriety brings a certain... clarity. Especially when things fall apart.<</dialog>>
<<dialog "player">>You sound like you're talking from experience.<</dialog>>
<<dialog "dinerManager">>A recent one. I prefer being the one in control of the wreckage.<</dialog>>
<<dialog "player">>If it's wreckage, isn't it already out of control?<</dialog>>
<<dialog "dinerManager">>Not if you're the one who planned the demolition.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
}
]
},
dinerRubys_dishwasherOnBreak_afternoon: {
front_break_afternoon: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is leaning against the host stand, watching Emma move across the floor. He's not doing anything, just observing.<</narrative>>
<<dialog "dinerManager">>The flow today is... adequate. But lacking soul.<</dialog>>
<<dialog "player">>Maybe everyone's just tired of the afternoon heat.<</dialog>>
<<dialog "dinerManager">>People don't get tired of good service. They just accommodate mediocrity.<</dialog>>
<<dialog "player">>You're very critical today.<</dialog>>
<<dialog "dinerManager">>I'm discerning. There's a difference.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is adjusting the alignment of the salt shakers at the counter, his touch light and precise. He stands close to you as you pass.<</narrative>>
<<dialog "dinerManager">>The details are what hold the world together, $player.firstName.<</dialog>>
<<dialog "player">>I think the glue might have something to do with it too.<</dialog>>
<<dialog "dinerManager">>I am the glue. And I notice when things are... out of place.<</dialog>>
<<dialog "player">>Is something out of place right now?<</dialog>>
<<dialog "dinerManager">>Not yet. But I'm watching.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontAfternoon-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Vince is holding a menu, pointing out a small smudge to a passing Sofia. He turns to you with a perfectly neutral expression.<</narrative>>
<<dialog "dinerManager">>Standards are difficult to maintain when nobody cares.<</dialog>>
<<dialog "player">>I think the staff cares. They're just human.<</dialog>>
<<dialog "dinerManager">>Human is a very convenient excuse for failure.<</dialog>>
<<dialog "player">>It's also why people come here. For the people.<</dialog>>
<<dialog "dinerManager">>They come for the food. They stay for the feeling of being handled by experts.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubys_dishwasherOnBreak_evening: {
front_break_evening: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is standing near the neon sign, the flickering light casting rhythmic shadows across his suit. He's looking out at the lot.<</narrative>>
<<dialog "dinerManager">>The parking lot looks so much larger at night.<</dialog>>
<<dialog "player">>Maybe because it's emptier.<</dialog>>
<<dialog "dinerManager">>It's a blank canvas. I could fit a whole terrace out there.<</dialog>>
<<dialog "player">>Vince, the lot is for cars. Not tables.<</dialog>>
<<dialog "dinerManager">>Cars are temporary. Experiences are permanent. Imagine the lighting, $player.firstName.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is checking the lock on the front door, even though it's still hours before closing. He stands just a bit too close as he talks.<</narrative>>
<<dialog "dinerManager">>Security is about knowing who shouldn't be here.<</dialog>>
<<dialog "player">>I hope I'm on the 'should be' list.<</dialog>>
<<dialog "dinerManager">>You're on the 'essential' list. Don't worry.<</dialog>>
<<dialog "player">>Essential is a big word for a dishwasher.<</dialog>>
<<dialog "dinerManager">>It's about the presence, not the position. You have... presence.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceFrontEvening-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Vince is straightening his cuffs while looking at his reflection in the mirrored backsplash behind the counter.<</narrative>>
<<dialog "dinerManager">>A man's image is his only true currency.<</dialog>>
<<dialog "player">>I thought you said time was the only true currency.<</dialog>>
<<dialog "dinerManager">>I have many currencies. Most people just carry change.<</dialog>>
<<dialog "player">>Well, keep your bills, Vince. I'm fine with change.<</dialog>>
<<dialog "dinerManager">>Ambition would suit you better than humility, $player.firstName.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
},
dinerRubysKitchen_dishwasherOnBreak_afternoon: {
kitchen_break_afternoon: [
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceKitchenAfternoon-onBreak-1.webp">>
<<image _img "100%">>
<<narrative>>Vince is standing near the pass window, staring at the grease traps. James is clearly ignoring him.<</narrative>>
<<dialog "dinerManager">>Efficiency is being strangled by this layout.<</dialog>>
<<dialog "player">>James seems to do fine with it.<</dialog>>
<<dialog "dinerManager">>James is used to it. That doesn't make it right.<</dialog>>
<<dialog "player">>Maybe don't tell him that while he's holding a knife.<</dialog>>
<<dialog "dinerManager">>James respects authority. Even when it's right.<</dialog>>`,
friendship: 1, trust: 0, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceKitchenAfternoon-onBreak-2.webp">>
<<image _img "100%">>
<<narrative>>Vince is inspecting a stack of 'clean' plates with a tiny flashlight he pulled from his pocket. He looks disappointed.<</narrative>>
<<dialog "dinerManager">>Water spots. The silent killer of quality.<</dialog>>
<<dialog "player">>The machine's been acting up, Vince. I told you.<</dialog>>
<<dialog "dinerManager">>Tools don't make mistakes. Maintenance does.<</dialog>>
<<dialog "player">>I'm doing my best here.<</dialog>>
<<dialog "dinerManager">>I know you are. But your best needs better tools. I'll see to it.<</dialog>>`,
friendship: 1, trust: 1, love: 0, lust: 0
},
{
text: `<<set _img = "assets/content/scenes/interactions/vince/level1/vinceKitchenAfternoon-onBreak-3.webp">>
<<image _img "100%">>
<<narrative>>Vince stands in the middle of the kitchen floor, hands behind his back, just watching people work. The atmosphere is thick.<</narrative>>
<<dialog "dinerManager">>They move differently when I'm here. It's fascinating.<</dialog>>
<<dialog "player">>They're nervous, Vince. You're hovering.<</dialog>>
<<dialog "dinerManager">>I'm providing an incentive to excel. Don't you feel it?<</dialog>>
<<dialog "player">>I feel like you're in the way of the trash can.<</dialog>>
<<dialog "dinerManager">>Honesty is your most captivating trait, $player.firstName.<</dialog>>`,
friendship: 2, trust: 0, love: 0, lust: 0
}
]
}
}>>
<</nobr>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<!-- Sidebar -->
<div class="prologue-sidebar">
<div class="prologue-sidebar-title">My Selections</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Early Years (0-5)</div>
<<if $prologueSelections.early>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.early.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.early.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Childhood (6-9)</div>
<<if $prologueSelections.childhood>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.childhood.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.childhood.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Formative (10-12)</div>
<<if $prologueSelections.formative>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.formative.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.formative.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title current">Adolescent (13-15)</div>
<<if $prologueSelections.adolescent>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.adolescent.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.adolescent.effect</div>
</div></div>
<<else>>
<div class="prologue-sidebar-empty">No selection yet</div>
<</if>>
</div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Coming of Age (16-17)</div></div>
</div>
<div class="prologue-content-area">
<div class="prologue-progress-container">
<div class="prologue-progress-bar">
<div class="prologue-progress-fill" style="width: 66.6%;"></div>
</div>
<div class="prologue-progress-steps">
<span class="prologue-progress-step completed">0-5</span>
<span class="prologue-progress-step completed">6-9</span>
<span class="prologue-progress-step completed">10-12</span>
<span class="prologue-progress-step active">13-15</span>
<span class="prologue-progress-step">16-17</span>
</div>
</div>
<div class="prologue-header">
<div class="prologue-age-badge">Ages 13-15</div>
<h1 class="prologue-title">Changes</h1>
<div class="prologue-header-line"></div>
<p class="prologue-subtitle">Everything was different now...</p>
</div>
<<image "assets/content/scenes/prologue/adolescentYears.webp" "100%">>
<<narrative>>
Adolescence. That strange, confusing time when everything changes at once.
<</narrative>>
<<narrative>>
Your body was different now. You'd stand in front of the mirror longer than you used to. Whether your jeans sat right mattered now, in a way it hadn't last summer.
<</narrative>>
<<narrative>>
At home, the tension was building.
<</narrative>>
<<narrative>>
Mom didn't see you as her little girl anymore, but she didn't treat you like an adult either. The questions were constant: "Where are you going?" "With who?" "When will you be back?" "What are you wearing?"
<</narrative>>
<<narrative>>
One Friday night you wanted to go to a party. Sam's basement. Most of your grade would be there.
<</narrative>>
<<narrative>>
"No," mom said flatly.
<</narrative>>
<<narrative>>
"Why not?" you shot back. "Everyone's going!"
<</narrative>>
<<narrative>>
"I said no. You're too young."
<</narrative>>
<<narrative>>
"I'm fourteen! When will I be old enough?"
<</narrative>>
<<narrative>>
The fight escalated. Voices raised. Your brother watched from the stairs, wide-eyed. Finally, dad intervened. "That's enough." He looked at mom. "Let her go. I'll pick her up at ten."
<</narrative>>
<<narrative>>
Mom's face went tight. "Fine. But if anything happens"
<</narrative>>
<<narrative>>
"Nothing will happen," you snapped, already heading for your room.
<</narrative>>
<<narrative>>
Later, dad drove you. The car was quiet for a while.
<</narrative>>
<<narrative>>
"Your mom worries," he said finally. "She just wants to protect you."
<</narrative>>
<<narrative>>
"She treats me like a kid."
<</narrative>>
<<narrative>>
"You're growing up fast. That scares her." He glanced at you. "Scares me too, if I'm honest."
<</narrative>>
<<narrative>>
You'd never heard him say something like that before. Not out loud.
<</narrative>>
<<narrative>>
Dad was distant in other ways though. He'd stopped asking about your life, stopped really engaging. Work consumed him. When he was home, he was tired, checked out. The conversation in the car felt like an exception, not the rule.
<</narrative>>
<<narrative>>
Your brother was going through his own changes. At eleven, twelve, thirteen, he became moody, locked himself in his room, wore headphones everywhere. Sometimes he'd seek you out: "How do you deal with mom?" Other times, he'd snap at you over nothing.
<</narrative>>
<<narrative>>
One night, after mom grounded him for bad grades, he knocked on your door.
<</narrative>>
<<narrative>>
"She hates me," he said, sitting on your bed.
<</narrative>>
<<narrative>>
"She doesn't hate you. She's just... mom."
<</narrative>>
<<narrative>>
"Does she do this to you?"
<</narrative>>
<<narrative>>
"All the time."
<</narrative>>
<<narrative>>
He looked at you, surprised. "Really?"
<</narrative>>
<<narrative>>
"Really. We're in this together, remember?"
<</narrative>>
<<narrative>>
He smiled, just a little. "Yeah. Together."
<</narrative>>
<<narrative>>
Friendships got complicated. Who sat with who at lunch became a coded map you had to read every morning. One ignored text and you'd spend the whole afternoon running worst-case scenarios.
<</narrative>>
<<narrative>>
The social hierarchy at school became more defined. Popular kids, outcasts, everyone in between. Where did you fit? Some days you felt confident, visible. Other days, completely invisible.
<</narrative>>
<<narrative>>
You got your first crush in eighth grade. You didn't tell anyone not your brother, definitely not mom. You weren't sure what to call it yet, and putting words on it would have made it real.
<</narrative>>
<<narrative>>
But your body knew. Your mind knew. Something was shifting, something bigger than school drama or fights with mom.
<</narrative>>
<<narrative>>
You were becoming someone new.
<</narrative>>
<div class="prologue-memory-section">
<div class="prologue-memory-section-title">Character-Shaping Memories</div>
<div class="prologue-memory-cards" id="adolescent-cards">
<div class="prologue-memory-card" data-section="adolescent" data-title="Mom's Tension" data-effect="+15% Beauty" data-stat="beauty" data-value="1.15">
<div class="prologue-memory-title">Mom's Tension</div>
<div class="prologue-memory-text">
"Appearance started to matter. You'd spend an hour getting ready for school, trying different outfits. Mom noticed. 'You don't need all that,' she'd say, wiping lipstick off your face. Then she'd catch herself in the mirror, touch her own hair. 'You're beautiful,' she'd add quietly. 'Don't grow up too fast.' The tension was always there. She wanted you to be confident but not too confident. You figured out the difference between looking put together for yourself and looking put together for someone else. Mostly by doing the second one first."
</div>
<div class="prologue-memory-effect">+15% Beauty</div>
</div>
<div class="prologue-memory-card" data-section="adolescent" data-title="Dad's Quiet Support" data-effect="+15% Social Skills" data-stat="social" data-value="1.15">
<div class="prologue-memory-title">Dad's Quiet Support</div>
<div class="prologue-memory-text">
"Your social life exploded during those years. Parties, sleepovers, hanging out at the mall. Dad would drive you everywhere, often in silence. He didn't understand your world anymore. The drama, the friendships, who was dating who. But he showed up. 'Call me when you're ready,' he'd say, handing you twenty bucks without looking at you. Like he was embarrassed to do it in front of mom. You never called him for pickup. You didn't need to. Just knowing you could was enough."
</div>
<div class="prologue-memory-effect">+15% Social Skills</div>
</div>
<div class="prologue-memory-card" data-section="adolescent" data-title="Your Space" data-effect="+15% Technical Skills" data-stat="technical" data-value="1.15">
<div class="prologue-memory-title">Your Space</div>
<div class="prologue-memory-text">
"The internet became an extension of you. A world your parents didn't fully understand. 'You're always on that thing,' mom would complain. Dad would shake his head, confused by what you found so fascinating. But online, you could be anyone. Find communities that got you. Your brother would ask: 'What are you doing?' 'You wouldn't understand,' you'd reply. You figured out how to look things up that your parents couldn't explain. That became a habit. They still ask you how to do things on the router."
</div>
<div class="prologue-memory-effect">+15% Technical Skills</div>
</div>
<div class="prologue-memory-card" data-section="adolescent" data-title="Beyond Family" data-effect="+15% Willpower" data-stat="willpower" data-value="1.15">
<div class="prologue-memory-title">Beyond Family</div>
<div class="prologue-memory-text">
"You figured out early that your parents weren't going to solve middle school for you. So you stopped asking. Friends became the thing that mattered the first people you told things to, before family. That's still how it works. Whether that's a strength or not depends on the day."
</div>
<div class="prologue-memory-effect">+15% Willpower</div>
</div>
</div>
</div>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-secondary">Back</span>' "formativeYears">><</link>>
<span id="adolescent-continue" class="prologue-btn prologue-btn-primary disabled">Continue</span>
</div>
</div>
</div>
</div>
</div>
<</nobr>>
<<script>>
$(document).ready(function() {
$('#adolescent-cards .prologue-memory-card').on('click', function() {
var $card = $(this);
var title = $card.data('title');
var effect = $card.data('effect');
var stat = $card.data('stat');
var value = $card.data('value');
$card.siblings().removeClass('selected');
$card.addClass('selected');
State.variables.prologueSelections.adolescent = {
title: title,
effect: effect,
stat: stat,
value: value
};
// Update sidebar - find the current section
var $currentSection = $('.prologue-sidebar-section-title.current').parent();
$currentSection.find('.prologue-sidebar-empty').remove();
$currentSection.find('.prologue-sidebar-item').remove();
$currentSection.append(
'<div class="prologue-sidebar-item"><div>' +
'<div class="prologue-sidebar-item-text">' + title + '</div>' +
'<div class="prologue-sidebar-item-effect">' + effect + '</div>' +
'</div></div>'
);
$('#adolescent-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('comingofAge');
});
});
var existing = State.variables.prologueSelections.adolescent;
if (existing) {
$('#adolescent-cards .prologue-memory-card').each(function() {
if ($(this).data('title') === existing.title) {
$(this).addClass('selected');
$('#adolescent-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('comingofAge');
});
}
});
}
});
<</script>>
<<nobr>><<set $location = "alleyBack">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Back Alley">>A dark alley behind the main strip. Shady deals and dangerous encounters.<</narrative>>
<<showLocationChars "alleyBack">>
<<navMenu>><<navCard "redLightCenter">><</navMenu>><<nobr>><<set $location = "apartmentComplex">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Brookside Apartments">>A run-down apartment building. Cheap rent attracts all kinds of residents.<</narrative>>
<<showLocationChars "apartmentComplex">>
<<navMenu>><<navCard "southside">><</navMenu>><<nobr>><<set $location = "apartmentsSuburbs">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Sunset Heights">>Low-income housing on the edge of town. Quiet but isolated.<</narrative>>
<<showLocationChars "apartmentsSuburbs">>
<<navMenu>><<navCard "suburbs">><</navMenu>><<silently>>
<<set $location = $activityOrigin || "fhBackyard">>
<<set $selectedDuration = parseInt($selectedDuration) || 30>>
<<set _restScale = $selectedDuration / 30>>
<<set _stressLoss = Math.max(1, Math.round(10 * _restScale))>>
<<set _moodGain = Math.max(1, Math.round(5 * _restScale))>>
<<set _energyGain = Math.max(1, Math.round(5 * _restScale))>>
<<set _stressBefore = $stress || 0>>
<<set _moodBefore = $mood || 0>>
<<set _energyBefore = $energy || 0>>
<<set $stress = Math.max(0, _stressBefore - _stressLoss)>>
<<set _actualStressLoss = _stressBefore - $stress>>
<<if _actualStressLoss > 0>><<queueStatChange "stress" (-1 * _actualStressLoss)>><</if>>
<<set $mood = Math.min(100, _moodBefore + _moodGain)>>
<<set _actualMoodGain = $mood - _moodBefore>>
<<if _actualMoodGain > 0>><<queueStatChange "mood" _actualMoodGain>><</if>>
<<set $energy = Math.min($energyMax || 100, _energyBefore + _energyGain)>>
<<set _actualEnergyGain = $energy - _energyBefore>>
<<if _actualEnergyGain > 0>><<queueStatChange "energy" _actualEnergyGain>><</if>>
<<advanceTime $selectedDuration "relax">>
<<flushNotifications>>
<<recalculateStats>>
<<run $(document).trigger(':passagerender')>>
<<set _vidNum = random(1, 2)>>
<<set _restVid = "assets/content/scenes/maplewood/park/rest/restingPark" + _vidNum + ".mp4">>
<</silently>>
<<narrative "Backyard Rest">>
You settle into a chair on the patio or find a spot on the grass. The garden is quietjust birds and the rustle of leaves. You let yourself unwind with no agenda. After a while you feel a bit more at ease.
<</narrative>>
<<vid _restVid "100%">>
<div class="location-actions">
<<btn "Continue" $location>><</btn>>
</div><<nobr>>
<<set $location = "bank">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "First National Bank">>
A grand marble building with high ceilings and polished floors. Security guards stand at attention near the entrance.
<</narrative>>
<<showLocationChars "bank">>
<<navMenu>>
<<navCard "towerB">>
<</navMenu>><<nobr>><<set $location = "barGloryHole">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Anonymous">>An extremely explicit bar with private booths. Only for the adventurous.<</narrative>>
<<showLocationChars "barGloryHole">>
<<navMenu>><<navCard "basementEntrance">><</navMenu>><<nobr>><<set $location = "barNeon">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Neon Nights">>A flashy bar with neon lights and loud music. Popular with the nightlife crowd.<</narrative>>
<<showLocationChars "barNeon">>
<<navMenu>><<navCard "basementEntrance">><</navMenu>><<nobr>><<set $location = "barWine">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Wine Cellar">>An elegant wine bar with an extensive selection. Sommelier recommendations and quiet conversations.<</narrative>>
<<showLocationChars "barWine">>
<<navMenu>><<navCard "streetFifth">><</navMenu>><<nobr>><<set $location = "basementEntrance">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Underground">>A shadowy staircase leading down. Invitation only, if you know the right people.<</narrative>>
<<showLocationChars "basementEntrance">>
<<navMenu>>
<<navCard "barNeon">>
<<navCard "clubStrip">>
<<navCard "barGloryHole">>
<<navCard "redLightCenter">>
<</navMenu>><<nobr>><<set $location = "beach">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Marina Beach">>Golden sand and rolling waves. Sunbathers relax while others play in the surf.<</narrative>>
<<showLocationChars "beach">>
<<navMenu>><<navCard "beachVolleyball">><<navCard "marinaBay">><</navMenu>><<nobr>><<set $location = "beachBar">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Sandbar">>A laid-back bar with tropical drinks and ocean views. Live music on weekends.<</narrative>>
<<showLocationChars "beachBar">>
<<navMenu>><<navCard "marinaBay">><</navMenu>><<nobr>><<set $location = "beachClub">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Yacht Club">>An exclusive members-only club with pool, cabanas, and VIP service.<</narrative>>
<<showLocationChars "beachClub">>
<<navMenu>><<navCard "marinaBay">><</navMenu>><<nobr>><<set $location = "beachVolleybal">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Beach Volleyball Courts">>Sandy courts where locals play pickup games. The competitive yet friendly atmosphere.<</narrative>>
<<showLocationChars "beachVolleybal">>
<<navMenu>><<navCard "beach">><</navMenu>><<widget "bedActions">>
<<nobr>>
<<set _currentHour = 0>>
<<set _energyLevel = 100>>
<<if $timeSys && $timeSys.hour !== undefined>>
<<set _currentHour = $timeSys.hour>>
<</if>>
<<if $energy !== undefined>>
<<set _energyLevel = $energy>>
<</if>>
<<set _masturbationUnlocked = ($flags && $flags.masturbationUnlocked)>>
<<set _masturbationReady = (_masturbationUnlocked && (($corruption || 0) >= 1) && (($arousal || 0) >= 70))>>
<div class="location-actions">
<<btnPicker "Sleep" "sleep_prep" "sleepDuration">>
<<set $activityOrigin = "fhBedroom">>
<<btnPicker "Nap" "runNap" "napDuration">>
<<btn "Set Alarm" "setAlarm">><</btn>>
<<if _masturbationUnlocked>>
<<if _masturbationReady>>
<<btn "Masturbate" "bedMasturbate">><</btn>>
<<else>>
<<btn "Masturbate" "" "locked" "Requires Arousal 70">><</btn>>
<</if>>
<</if>>
</div>
<</nobr>>
<</widget>>
/* ==========================================
WAKE-UP OPTIONS WIDGET
Renders the post-sleep choices for the player.
Reads $sleepWakeContext (set by sleep.twee and its follow-ups) to decide
which continue-sleep buttons to show:
- Sleep to Desired Time : shown when planned duration not yet reached
- Sleep Until Alarm : shown when alarm was set and has not fired yet
- Snooze : shown whenever energy is below max
========================================== */
<<widget "bedWakeUp">>
<<nobr>>
<<silently>>
/* Clothing state */
<<set _isNakedAtWakeup = false>>
<<set _isUnderwearOnlyAtWakeup = false>>
<<if $wardrobe && $wardrobe.equipped>>
<<set _hasOuterCovering = (
($wardrobe.equipped.sleepwear && $wardrobe.equipped.sleepwear !== "") ||
($wardrobe.equipped.top && $wardrobe.equipped.top !== "") ||
($wardrobe.equipped.dress && $wardrobe.equipped.dress !== "") ||
($wardrobe.equipped.bottom && $wardrobe.equipped.bottom !== "")
)>>
<<set _hasUnderwear = (
($wardrobe.equipped.bra && $wardrobe.equipped.bra !== "") ||
($wardrobe.equipped.panty && $wardrobe.equipped.panty !== "")
)>>
<<set _isNakedAtWakeup = (!_hasOuterCovering && !_hasUnderwear)>>
<<set _isUnderwearOnlyAtWakeup = (!_hasOuterCovering && _hasUnderwear)>>
<</if>>
/* QoL flags from sleep context */
<<set _ctx = $sleepWakeContext>>
<<if ndef _ctx>><<set _ctx = {}>><</if>>
<<set _reqDur = parseInt(_ctx.requestedDuration, 10)>>
<<if _reqDur !== _reqDur>><<set _reqDur = 0>><</if>>
<<set _slept = parseInt(_ctx.sleepDuration, 10)>>
<<if _slept !== _slept>><<set _slept = 0>><</if>>
<<set _alarmCtx = _ctx.alarmActive ? true : false>>
<<set _tUntilAlarm = parseInt(_ctx.timeUntilAlarm, 10)>>
<<if _tUntilAlarm !== _tUntilAlarm>><<set _tUntilAlarm = 0>><</if>>
<<set _energyNow = parseInt($energy || 0, 10)>>
<<set _energyMaxNow = parseInt($energyMax || 80, 10)>>
<<set _reachedPlan = _slept >= _reqDur>>
<<set _reachedAlarm = (!_alarmCtx) || (_slept >= _tUntilAlarm)>>
<<set _energyFull = _energyNow >= _energyMaxNow>>
<<set _showSleepToDesired = !_reachedPlan && _reqDur > 0>>
<<set _showSleepUntilAlarm = _alarmCtx && !_reachedAlarm>>
<<set _showSnooze = !_energyFull>>
<</silently>>
<<if _showSleepToDesired || _showSleepUntilAlarm || _showSnooze>>
<div class="location-actions">
<<if _showSleepToDesired>>
<<btn "Sleep to Desired Time" "sleepToDesiredTime">><</btn>>
<</if>>
<<if _showSleepUntilAlarm>>
<<btn "Sleep Until Alarm" "sleepUntilAlarm">><</btn>>
<</if>>
<<if _showSnooze>>
<<btnPicker "Snooze" "sleepSnoozeApply" "snoozeDuration">>
<</if>>
</div>
<</if>>
<div class="location-actions">
<<btn "Get Dressed" "fhBedroom">>
<<if $preSleepOutfit>>
<<set $wardrobe.equipped = clone($preSleepOutfit)>>
<<recalculateStats>>
<<updateClothesNotification>>
<</if>>
<</btn>>
<<if _isNakedAtWakeup && $corruption < 5>>
<<btn "Go to Bathroom" "sleep_naked_bathroom_confirm">><</btn>>
<<elseif _isUnderwearOnlyAtWakeup && $corruption < 3>>
<<btn "Go to Bathroom" "sleep_naked_bathroom_confirm">><</btn>>
<<else>>
<<btn "Go to Bathroom" "fhUpperBath">><</btn>>
<</if>>
<<btn "Get Out of Bed" "fhBedroom">><</btn>>
</div>
<</nobr>>
<</widget>>
<<nobr>><<set $location = "boutiqueA">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Maison �lise">>A high-end fashion boutique with exclusive designer pieces. Personal stylists on call.<</narrative>>
<<showLocationChars "boutiqueA">>
<<navMenu>><<navCard "streetFifth">><</navMenu>><<nobr>><<set $location = "boutiqueB">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Aurum Couture">>Luxury accessories and jewelry. Handcrafted items from renowned artisans.<</narrative>>
<<showLocationChars "boutiqueB">>
<<navMenu>><<navCard "streetFifth">><</navMenu>><<nobr>><<set $location = "boutiqueC">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Bellucci Milano">>Premium cosmetics and skincare. The latest in beauty innovation.<</narrative>>
<<showLocationChars "boutiqueC">>
<<navMenu>><<navCard "streetFifth">><</navMenu>><<nobr>>
<<silently>>
<<set _returnPassage = $porn.returnPassage || "brotherComputerMenu">>
<<if $porn.source === "brotherRoom">>
<<set $location = "fhBrotherRoom">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set _canFirst = !$flags.brotherPornFirstSeen && (($corruption || 0) >= 1)>>
<<set _phase2Step = $brotherPornPhase2Step || 0>>
<<set _canSecond = $flags.brotherPornFirstSeen && !$flags.brotherPornSecondPeek && (_phase2Step < 4) && (($corruption || 0) >= 1) && (($brotherPornSecondLastSeenDayKey || 0) !== _dateKey)>>
<<if $porn.source === "brotherRoom">>
<<if _canFirst && random(1, 100) <= 33>>
<<goto "brotherPC_pornFirst_discovery">>
<</if>>
<<if _canSecond && random(1, 100) <= 50>>
<<goto "brotherPC_pornSecond_curiosity">>
<</if>>
<</if>>
<<set _v = random(1, 8)>>
<<switch _v>>
<<case 1>>
<<set _sceneText = "You fall into a social feed - friends posting highlights, ads for things you don't need, and a debate in the comments that absolutely no one will win. It's dumb. It's kind of comforting.">>
<<case 2>>
<<set _sceneText = "Local news sites load slowly, then hit you with a headline about the town council and someone's escaped emu. You read three paragraphs before you realize you care more than you expected.">>
<<case 3>>
<<set _sceneText = "You skim memes, reaction clips, and a thread explaining a show you've never watched. Half an hour vanishes. Your brain feels lightly toasted, in a good way.">>
<<case 4>>
<<set _sceneText = "Someone you barely know posted a vacation album. You scroll anyway - beach, food, a blurry concert. It's shallow. It still makes the room feel a little bigger than your brother's mess.">>
<<case 5>>
<<set _sceneText = "A drama explodes in a group chat screenshot someone shared publicly. You tell yourself you'll stop reading. You do not stop reading.">>
<<case 6>>
<<set _sceneText = "You start with a recipe video, drift into a cooking competition clip, then somehow end up watching a guy restore rusty tools. The algorithm knows you better than you want to admit.">>
<<case 7>>
<<set _sceneText = "The city forum is arguing about parking, dogs, and whether the new mall is evil. Nobody agrees. Everyone types like they're writing a manifesto. You lurk and snack on the chaos.">>
<<case 8>>
<<set _sceneText = "You put on a stream of random cute animals - puppies, parrots, a goat wearing a blanket. Simple. Soft. Exactly what you needed.">>
<</switch>>
<<advanceTime 30>>
<<gainStat "mood" 10>>
<<loseStat "stress" 8>>
<<loseStat "energy" 5>>
<<flushNotifications>>
<</silently>>
<<narrative "Browsing">>
<<print _sceneText>>
<</narrative>>
<div class="location-actions">
<<btn "Go Back" _returnPassage>><</btn>>
</div>
<</nobr>>
<<nobr>>
<<silently>>
<<updateCharacterLocations>>
<<set _brotherOnPC = !$_navigatingBackward && $characters.brother && $characters.brother.currentLocation === "fhBrotherRoom">>
<<if _brotherOnPC>>
<<run window.notifyWarning("Your brother walked in and made you get off the computer.")>>
<<set $location = "fhBrotherRoom">>
<<goto "fhBrotherRoom">>
<</if>>
<<set $location = "fhBrotherRoomPC">>
<<set $porn.source = "brotherRoom">>
<<set $porn.returnPassage = "brotherComputerMenu">>
<</silently>>
<<narrative "Brother's Computer">>
You sit at your brother's messy desk. The machine whirs to life and his anime wallpaper fills the screen - icons everywhere, half of them games.
What do you want to do?
<</narrative>>
<div class="location-actions">
<<if $flags.brotherPornLookButtonUnlocked && ($corruption || 0) >= 1 && !_brotherOnPC>>
<<btn "Watch Porn" "pornSite_hotHub" "default">><</btn>>
<</if>>
<<btn "Browse / Surf" "brotherComputerBrowse" "default" 15>><</btn>>
<<btn "Watch Video" "brotherComputerWatchVideo" "default" 12>><</btn>>
<<btn "Play Game" "brotherComputerPlayGame" "default" 15>><</btn>>
<<btn "Close Computer" "fhBrotherRoom">><</btn>>
</div>
<</nobr>>
<<nobr>>
<<set _returnPassage = $porn.returnPassage || "brotherComputerMenu">>
<<requireMinEnergy 20 _returnPassage>>
<<silently>>
<<if $porn.source === "brotherRoom">>
<<set $location = "fhBrotherRoom">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<set _pcVidBase = "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/gaming/">>
<<set _sceneVid = "">>
<<set _v = random(1, 4)>>
<<switch _v>>
<<case 1>>
<<set _sceneText = "You load something competitive - matches, ranks, people typing faster than you can think. Win or lose, your pulse stays up until you force yourself to quit one more game.">>
<<set _sceneVid = _pcVidBase + "gameComp" + random(1, 6) + ".mp4">>
<<case 2>>
<<set _sceneText = "A cozy builder or farm sim eats the hour. Plant, craft, repeat. Numbers go up. Your brain likes the little rewards more than you'd admit.">>
<<set _sceneVid = _pcVidBase + "gameCozy" + random(1, 5) + ".mp4">>
<<case 3>>
<<set _sceneText = "A roguelike punishes you, tempts you with one more run, then punishes you again. The loop is stupid. The loop is perfect.">>
<<set _sceneVid = _pcVidBase + "gameRogue" + random(1, 9) + ".mp4">>
<<case 4>>
<<set _sceneText = "You sink into a story game - dialogue choices, slow exploration, a plot that actually lands. When you look up, the room feels dimmer and your eyes are dry.">>
<<set _sceneVid = _pcVidBase + "gameStory" + random(1, 14) + ".mp4">>
<</switch>>
<<advanceTime 30 "relax">>
<<gainStat "mood" 10>>
<<loseStat "stress" 10>>
<<loseStat "energy" 10>>
<<set _gamingNow = ($skills.technical && def $skills.technical.gaming) ? $skills.technical.gaming : 0>>
<<if _gamingNow >= 25>>
<<run window.notifyWarning("You've gained all the gaming skill you can from playing solo on your brother's PC.")>>
<<else>>
<<gainSkill "technical" "gaming" 1>>
<</if>>
<<flushNotifications>>
<</silently>>
<<narrative "Gaming">>
<<print _sceneText>>
<</narrative>>
<<if _sceneVid !== "">><<vid _sceneVid "100%">><</if>>
<div class="location-actions">
<<btn "Go Back" _returnPassage>><</btn>>
</div>
<</nobr>>
<<nobr>>
<<set _returnPassage = $porn.returnPassage || "brotherComputerMenu">>
<<requireMinEnergy 17 _returnPassage>>
<<silently>>
<<if $porn.source === "brotherRoom">>
<<set $location = "fhBrotherRoom">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<set _v = random(1, 4)>>
<<switch _v>>
<<case 1>>
<<set _sceneText = "You queue up a long video - someone's playthrough with commentary, or a rambling essay about a thing you half-know. The tab stays open. The world shrinks to the screen.">>
<<case 2>>
<<set _sceneText = "You let a playlist run: music, live sessions, grainy concert footage. Your shoulders drop. The messy desk fades into the background.">>
<<case 3>>
<<set _sceneText = "One episode turns into another. The show isn't even that good, but the rhythm of it - intro, problem, resolution - pulls you along until the credits roll again.">>
<<case 4>>
<<set _sceneText = "A documentary about weather, or food, or some niche history plays. Calm voiceover, pretty shots. You learn three facts you'll forget and still feel better.">>
<</switch>>
<<advanceTime 45 "relax">>
<<gainStat "mood" 15>>
<<loseStat "stress" 12>>
<<loseStat "energy" 7>>
<<flushNotifications>>
<</silently>>
<<narrative "Watching Video">>
<<print _sceneText>>
<</narrative>>
<div class="location-actions">
<<btn "Go Back" _returnPassage>><</btn>>
</div>
<</nobr>>
/* ==========================================
EVENT - Beauty Search (Something Different arc)
Player researches skincare at brother's PC.
Cinematic, single passage, no menu.
Triggers from quest: something_different stage 2
Voice: <<narrative>> = narrator to player (second person *you* is fine); present in-scene;
inner voice = <<dialog "player">> + (Inner voice), first person (*I*).
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhBrotherRoomPC">>
<<advanceTime 45>>
<<advanceQuestStage "something_different">>
<</silently>>
<<narrative "Brother's Room">>
You sit down slowly in his chair. The room is completely dark.
Only the cold blue light of the screen lights up your face.
You stare at the search bar for a moment.
<</narrative>>
<<vid "assets/content/scenes/events/diana/brotherPc.mp4" "100%">>
<<narrative>>
skincare routine for beginners
<</narrative>>
<<narrative>>
You press Enter.
<</narrative>>
<<narrative>>
The page fills instantly.
<</narrative>>
<<narrative>>
Videos, sponsored content, "beginner skincare" headlines...
<</narrative>>
<<narrative>>
You click the first one.
<</narrative>>
<<narrative>>
A woman. Looking directly at the camera. Her face bare, her hair pulled back.
Her voice is calm.
<</narrative>>
<<narrative>>
"I didn't know anything either. Really."
<</narrative>>
<<narrative>>
You watch.
Morning routine, evening routine, which product does what... step by step.
<</narrative>>
<<narrative>>
When the prices appear in the corner of the screen, your throat tightens.
<</narrative>>
<<narrative>>
You scroll to the comments.
"Wish someone told me this at 18."
<</narrative>>
<<dialog "player">>
(Inner voice)
Nobody told me either.
<</dialog>>
<<narrative>>
A suggestion catches your eye on the right: the same woman, two photos side by side.
<</narrative>>
<<narrative>>
You open it.
<</narrative>>
<<narrative>>
The one on the left feels familiar. You look at the one on the right longer.
<</narrative>>
<<narrative>>
There is no dramatic change.
But there is a difference.
Her face looks more at ease. Like she's finally started to resemble herself.
<</narrative>>
<<narrative>>
You click a link. Then another. Forum, another video, a product page...
<</narrative>>
<<narrative>>
Click. Stop. Go back. Click again.
<</narrative>>
<<narrative>>
At some point you look at the top of the browser.
The tabs have multiplied so much that the titles are gone only small icons remain.
<</narrative>>
<<narrative>>
A sentence catches your eye:
<</narrative>>
<<narrative>>
"You can tell when someone puts in effort and when they don't."
<</narrative>>
<<narrative>>
Vince's voice echoes in your head.
<</narrative>>
<<dialog "player">>
(Inner voice)
"Do you ever look at yourself in the mirror?"
<</dialog>>
<<narrative>>
Without realizing when it happened, you're on a store page.
<</narrative>>
<<dialog "player">>
(Inner voice)
Look at these prices...
Every month?
I haven't even gotten to the clothes yet.
<</dialog>>
<<narrative>>
Your mother comes to mind.
<</narrative>>
<<narrative>>
What does she do in the mornings? How long does she look in the mirror? Creams, products are those things even in the house?
<</narrative>>
<<dialog "player">>
(Inner voice)
I don't actually know. I never looked.
<</dialog>>
<<narrative>>
Outside, a car door slams.
<</narrative>>
<<narrative>>
You flinch. Your shoulders tense.
<</narrative>>
<<narrative>>
You turn back to the screen.
The tabs are still open. The prices are still the same.
<</narrative>>
<<dialog "player">>
(Inner voice)
I have money. Just not enough for all of this.
<</dialog>>
<div class="location-actions">
<<btn "Close and leave" "fhBrotherRoom">><</btn>>
</div>
</div><<nobr>>
<<requireMinEnergy 20 "fhBrotherRoom">>
<<silently>>
<<set $location = "fhBrotherRoom">>
<<set _pcVidBase = "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/gaming/">>
<<set _sceneVid = "">>
<<set _v = random(1, 4)>>
<<switch _v>>
<<case 1>>
<<set _sceneText = "He carries harder than he admits, but when you clutch a round he actually laughssurprised, not mean. The next match you play looser, talking smack like you've got the rank to back it up.">>
<<set _sceneVid = _pcVidBase + "gameComp" + random(1, 6) + ".mp4">>
<<case 2>>
<<set _sceneText = "You take turns on one cozy builder save. He pretends not to care where you put the decorations; he fixes your mistakes with quick clicks and worse excuses.">>
<<set _sceneVid = _pcVidBase + "gameCozy" + random(1, 5) + ".mp4">>
<<case 3>>
<<set _sceneText = "Co-op roguelike: you die first every time until you don't. He says 'told you' and queues another run anyway.">>
<<set _sceneVid = _pcVidBase + "gameRogue" + random(1, 9) + ".mp4">>
<<case 4>>
<<set _sceneText = "Story mode with two controllers mapped awkwardly to one keyboard. You argue over dialogue choices and agree the ending still hits.">>
<<set _sceneVid = _pcVidBase + "gameStory" + random(1, 14) + ".mp4">>
<</switch>>
<<gainStat "mood" 15>>
<<loseStat "stress" 10>>
<<loseStat "energy" 10>>
<<gainCharacterStat "brother" "friendship" 1>>
<<set _gamingNow = ($skills.technical && def $skills.technical.gaming) ? $skills.technical.gaming : 0>>
<<if _gamingNow >= 35>>
<<run window.notifyWarning("You've gained all the gaming skill you can from playing games with your brother.")>>
<<else>>
<<gainSkill "technical" "gaming" 2>>
<</if>>
<<recalculateStats>>
<<advanceTime 30 "relax">>
<<flushNotifications>>
<</silently>>
<<narrative "Gaming together">>
<<print _sceneText>>
<</narrative>>
<<if _sceneVid !== "">><<vid _sceneVid "100%">><</if>>
<div class="location-actions">
<<btn "Step Away" "fhBrotherRoom">><</btn>>
</div>
<</nobr>><<silently>>
<<set $location = "fhBackyard">>
<<logDailyActivity "brother" "talk">>
<<set _char = $characters.brother>>
<<set _stats = $characters.brother.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase */
<<if def $flags && $flags.summerBreak>>
<<set _phase = "vacation">>
<<else>>
<<set _phase = "school">>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Build context key
Backyard only exists in vacation phase: afternoon, evening
School phase has no backyard content fallback to vacation or livingroom */
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<if _phase === "vacation">>
<<if _timeSlot === "morning">>
/* Backyard morning → afternoon (he's barely outside in the morning) */
<<set _ctxKey = "fhBackyard_vacation_afternoon">>
<<else>>
<<set _ctxKey = "fhBackyard_" + _phase + "_" + _timeSlot>>
<</if>>
<<if setup.brotherTopics.vacation && setup.brotherTopics.vacation[_levelKey] && setup.brotherTopics.vacation[_levelKey][_ctxKey]>>
<<set _topicPool = setup.brotherTopics.vacation[_levelKey][_ctxKey]>>
<</if>>
<</if>>
/* School phase has no backyard use vacation afternoon as fallback */
<<if !_topicPool>>
<<if setup.brotherTopics.vacation && setup.brotherTopics.vacation[_levelKey] && setup.brotherTopics.vacation[_levelKey].fhBackyard_vacation_afternoon>>
<<set _topicPool = setup.brotherTopics.vacation[_levelKey].fhBackyard_vacation_afternoon>>
<</if>>
<</if>>
/* Absolute last resort: living room school afternoon */
<<if !_topicPool>>
<<set _topicPool = setup.brotherTopics.school.level1.fhLivingroom_school_afternoon>>
<</if>>
/* Pick topic */
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
/* Time and base stats */
<<advanceTime 20>>
<<loseStat "energy" 5>>
<<gainStat "mood" 4>>
<<flushNotifications>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "brother" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "brother" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "brother" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "brother" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.brother.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhBrotherRoom">>
<<logDailyActivity "brother" "talk">>
<<set _char = $characters.brother>>
<<set _stats = $characters.brother.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase */
<<if def $flags && $flags.summerBreak>>
<<set _phase = "vacation">>
<<else>>
<<set _phase = "school">>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Build context key: fhBrotherRoom_phase_timeSlot */
<<set _ctxKey = "fhBrotherRoom_" + _phase + "_" + _timeSlot>>
<<set _levelKey = "level" + _level>>
/* Resolve topic pool fallback chain */
<<set _topicPool = null>>
<<if setup.brotherTopics[_phase] && setup.brotherTopics[_phase][_levelKey] && setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<else>>
/* Fallback: school uses afternoon → evening for bedroom */
<<if _phase === "school" && _timeSlot === "morning">>
<<set _ctxKey = "fhBrotherRoom_school_afternoon">>
<<elseif _phase === "school" && _timeSlot === "afternoon">>
<<set _ctxKey = "fhBrotherRoom_school_evening">>
<<elseif _phase === "vacation" && _timeSlot === "morning">>
<<set _ctxKey = "fhBrotherRoom_vacation_afternoon">>
<<else>>
<<set _ctxKey = "fhBrotherRoom_" + _phase + "_evening">>
<</if>>
<<if setup.brotherTopics[_phase] && setup.brotherTopics[_phase][_levelKey] && setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
/* Last resort: school afternoon level1 */
<<if !_topicPool>>
<<set _topicPool = setup.brotherTopics.school.level1.fhBrotherRoom_school_afternoon>>
<</if>>
/* Pick a random topic group, then a random variation */
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "brother" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "brother" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "brother" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "brother" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.brother.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhKitchen">>
<<logDailyActivity "brother" "talk">>
<<set _char = $characters.brother>>
<<set _stats = $characters.brother.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase */
<<if def $flags && $flags.summerBreak>>
<<set _phase = "vacation">>
<<else>>
<<set _phase = "school">>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Build context key */
<<set _ctxKey = "fhKitchen_" + _phase + "_" + _timeSlot>>
<<set _levelKey = "level" + _level>>
/* Resolve topic pool
Kitchen has: school_morning, school_evening
vacation_morning, vacation_evening
Afternoon falls back to evening (no afternoon kitchen context) */
<<set _topicPool = null>>
<<if setup.brotherTopics[_phase] && setup.brotherTopics[_phase][_levelKey] && setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<else>>
/* Afternoon → evening fallback for kitchen */
<<if _timeSlot === "afternoon">>
<<set _ctxKey = "fhKitchen_" + _phase + "_evening">>
<<else>>
<<set _ctxKey = "fhKitchen_" + _phase + "_morning">>
<</if>>
<<if setup.brotherTopics[_phase] && setup.brotherTopics[_phase][_levelKey] && setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
/* Last resort */
<<if !_topicPool>>
<<set _topicPool = setup.brotherTopics.school.level1.fhKitchen_school_evening>>
<</if>>
/* Pick topic */
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "brother" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "brother" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "brother" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "brother" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.brother.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhLivingroom">>
<<logDailyActivity "brother" "talk">>
<<set _char = $characters.brother>>
<<set _stats = $characters.brother.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase */
<<if def $flags && $flags.summerBreak>>
<<set _phase = "vacation">>
<<else>>
<<set _phase = "school">>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Build context key */
<<set _ctxKey = "fhLivingroom_" + _phase + "_" + _timeSlot>>
<<set _levelKey = "level" + _level>>
/* Resolve topic pool
school: afternoon, evening (no morning he's at school)
vacation: morning, afternoon, evening */
<<set _topicPool = null>>
<<if setup.brotherTopics[_phase] && setup.brotherTopics[_phase][_levelKey] && setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<else>>
/* school morning → afternoon, vacation has all three */
<<if _phase === "school" && _timeSlot === "morning">>
<<set _ctxKey = "fhLivingroom_school_afternoon">>
<<else>>
<<set _ctxKey = "fhLivingroom_" + _phase + "_evening">>
<</if>>
<<if setup.brotherTopics[_phase] && setup.brotherTopics[_phase][_levelKey] && setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.brotherTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
/* Last resort */
<<if !_topicPool>>
<<set _topicPool = setup.brotherTopics.school.level1.fhLivingroom_school_afternoon>>
<</if>>
/* Pick topic */
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "brother" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "brother" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "brother" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "brother" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.brother.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "fhBrotherRoom">>
<<set _broHere = $characters.brother && $characters.brother.currentLocation === "fhBrotherRoom">>
<<set _gamingLvl = ($skills.technical && def $skills.technical.gaming) ? $skills.technical.gaming : 0>>
<<set _watchScene = random(1, 2)>>
<<set _watchBrotherVid = "">>
<<if _watchScene === 1>>
<<set _watchBrotherVid = "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/watchbrother/normal" + random(1, 2) + ".mp4">>
<<elseif _watchScene === 2>>
<<set _watchBrotherVid = "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/watchbrother/gaming" + random(1, 3) + ".mp4">>
<</if>>
<</silently>>
<<if !_broHere>>
<<goto "fhBrotherRoom">>
<<else>>
<<narrative "Brother's Room">>
<<switch _watchScene>>
<<case 1>>
He's at his desk, mouse moving in slow dragsscrolling feeds, a forum thread, something with too many ads. Normal PC noise: clicks, the occasional snort at a dumb comment. He's absorbed, not gaming.
<<case 2>>
His headset's on and he's leaned in, fingers busy on the keyboard. The screen throws colored light across his face. Every so often he mutters under his breath at whatever's happening in-game.
<</switch>>
<</narrative>>
<<if _watchBrotherVid !== "">><<vid _watchBrotherVid "100%">><</if>>
<div class="location-actions">
<<if _watchScene === 2>>
<<silently>>
<<set _canAskPlay = _gamingLvl >= 15 && (parseInt($energy || 0, 10) >= 20)>>
<<set _askPlayErrors = []>>
<<if _gamingLvl < 15>>
<<run _askPlayErrors.push("Need 15 gaming")>>
<</if>>
<<if parseInt($energy || 0, 10) < 20>>
<<run _askPlayErrors.push("Need 20 energy")>>
<</if>>
<<set _askPlayTooltip = _askPlayErrors.join(" ")>>
<</silently>>
<<if _canAskPlay>>
<<btn "Ask to play" "brotherPlayTogether" "default">><</btn>>
<<else>>
<<set _askPlayLockedSpan = '<span class="link-internal btn-style btn-default locked" data-tooltip="' + _askPlayTooltip + '"><i class="icon icon-lock icon-12"></i> Ask to play</span>'>>
<<print _askPlayLockedSpan>>
<</if>>
<</if>>
<<btn "Go back" "fhBrotherRoom">><</btn>>
</div>
<<silently>>
<<loseStat "energy" 3>>
<<gainStat "mood" 5>>
<<advanceTime 10>>
<<flushNotifications>>
<</silently>>
<</if>>
<</nobr>><<nobr>><<set $location = "cafeteriaUni">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Student Union">>A bustling cafeteria with affordable food. Students gather between classes.<</narrative>>
<<showLocationChars "cafeteriaUni">>
<<navMenu>><<navCard "universityDistrict">><</navMenu>><<nobr>><<set $location = "campus">><</nobr>>
<<updateCharacterLocations>>
<<narrative "University Campus">>The main university quad. Students study on the grass and rush between classes.<</narrative>>
<<showLocationChars "campus">>
<<navMenu>><<navCard "universityDistrict">><</navMenu>>/* ==========================================
BROTHER - Jake Taylor
Complete Character Definition
========================================== */
/* CHARACTER CONFIG (setup - survives version updates) */
<<set setup.characterDefs.brother = {
maxLevels: { friendship: 2, love: 1, trust: 1, lust: 1 },
versionCaps: { friendship: 100, love: 15, trust: 50, lust: 0 }
}>>
/* CHARACTER DATA */
<<set $characters.brother = {
firstName: "Jake",
lastName: "Taylor",
birthYear: 2009,
gender: "male",
occupation: "High School Student",
location: "Home, His Bedroom",
avatar: "assets/content/people/family/brother/brother.webp",
type: "npc",
color: "#10b981",
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
status: "Brother",
firstMet: "Childhood",
info: "<p>Your younger brother who goes to high school. He's usually gaming or hanging out in his room.</p>",
known: true,
currentLocation: null,
currentStatus: null,
relationship: { status: "single" }
}>>
/* SCHEDULE - Where is he at different times */
/* Location IDs match Navigation[INIT].twee */
/* school: During school term */
/* vacation: Vacation periods */
<<set setup.schedules.brother = {
school: {
weekday: [
{ hour: 0, location: "fhBrotherRoom", status: "sleeping" },
{ hour: 6, minute: 50, location: "fhUpperBath", status: "showering" },
{ hour: 7, location: "fhKitchen", status: "available" },
{ hour: 7, minute: 30, location: "fhBrotherRoom", status: "available" },
{ hour: 8, location: "school", status: "busy" },
{ hour: 15, location: "fhBrotherRoom", status: "available", meetup: true },
{ hour: 17, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 18, location: "fhKitchen", status: "available" },
{ hour: 19, minute: 30, location: "fhBrotherRoom", status: "available", meetup: true },
{ hour: 20, location: "fhBrotherRoom", status: "available", meetup: true },
{ hour: 23, location: "fhBrotherRoom", status: "sleeping" }
],
weekend: [
{ hour: 0, location: "fhBrotherRoom", status: "sleeping" },
{ hour: 8, location: "fhUpperBath", status: "showering" },
{ hour: 8, minute: 15, location: "fhBrotherRoom", status: "available" },
{ hour: 8, minute: 30, location: "fhKitchen", status: "available" },
{ hour: 9, minute: 30, location: "fhBrotherRoom", status: "available" },
{ hour: 11, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 12, minute: 30, location: "fhKitchen", status: "available" },
{ hour: 13, minute: 30, location: "fhBrotherRoom", status: "available" },
{ hour: 14, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 18, location: "fhKitchen", status: "available" },
{ hour: 19, minute: 30, location: "fhBrotherRoom", status: "available", meetup: true },
{ hour: 20, location: "fhBrotherRoom", status: "available", meetup: true },
{ hour: 23, location: "fhBrotherRoom", status: "sleeping" }
]
},
vacation: {
weekday: [
{ hour: 0, location: "fhBrotherRoom", status: "available" }, /* Late night gaming */
{ hour: 2, location: "fhBrotherRoom", status: "sleeping" },
{ hour: 6, minute: 50, location: "fhUpperBath", status: "showering" }, /* Shower 6:50-7:00 (10 min) */
{ hour: 7, location: "fhKitchen", status: "available" }, /* Family Breakfast 7:00-7:30 */
{ hour: 7, minute: 30, location: "fhBrotherRoom", status: "available" },
{ hour: 10, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 12, minute: 30, location: "fhKitchen", status: "available" }, /* Family Lunch 12:30-13:00 */
{ hour: 13, location: "fhBrotherRoom", status: "available" },
{ hour: 15, location: "fhBackyard", status: "available", meetup: true }, /* Backyard - summer vibes */
{ hour: 16, location: "fhLivingroom", status: "available" },
{ hour: 18, location: "fhKitchen", status: "available" }, /* Family Dinner 18:30-19:30 */
{ hour: 19, minute: 30, location: "fhBackyard", status: "available", meetup: true }, /* Evening backyard */
{ hour: 20, minute: 30, location: "fhBrotherRoom", status: "available" },
{ hour: 23, location: "fhBrotherRoom", status: "available" } /* Still up */
],
weekend: [
{ hour: 0, location: "fhBrotherRoom", status: "available" }, /* Late night gaming */
{ hour: 2, location: "fhBrotherRoom", status: "sleeping" },
{ hour: 8, location: "fhUpperBath", status: "showering" }, /* Shower 8:00-8:15 (15 min) */
{ hour: 8, minute: 15, location: "fhBrotherRoom", status: "available" }, /* Getting ready */
{ hour: 8, minute: 30, location: "fhKitchen", status: "available" }, /* Family Breakfast 8:30-9:30 */
{ hour: 9, minute: 30, location: "fhBrotherRoom", status: "available" },
{ hour: 11, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 12, minute: 30, location: "fhKitchen", status: "available" }, /* Family Lunch 12:30-13:30 */
{ hour: 13, minute: 30, location: "fhBrotherRoom", status: "available" },
{ hour: 15, location: "fhBackyard", status: "available", meetup: true }, /* Backyard - summer vibes */
{ hour: 16, location: "fhLivingroom", status: "available" },
{ hour: 18, location: "fhKitchen", status: "available" }, /* Family Dinner 18:30-19:30 */
{ hour: 19, minute: 30, location: "fhBackyard", status: "available", meetup: true }, /* Evening backyard */
{ hour: 20, minute: 30, location: "fhBrotherRoom", status: "available" },
{ hour: 23, location: "fhBrotherRoom", status: "available" } /* Still up */
]
}
}>>
/* ACTIONS - What can you do with him at each location */
/* Uses setup.* to avoid save file bloat */
<<set setup.characterActions.brother = {
fhBrotherRoom: [
{
id: "vinceDay3FamilyBrother",
label: "Need to vent",
passage: "quest_vince_day3_family_brother",
tags: [],
requirements: { flag: "vinceDay3FamilyOpen", flagNot: "vinceDay3FamilyBrotherDone" },
dailyLimit: false, minPlayerEnergy: 5, showWhenLocked: false, questButton: true
},
{
id: "talk", label: "Talk", passage: "brotherTalkBedroom",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhKitchen: [
{
id: "talk", label: "Talk", passage: "brotherTalkKitchen",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhLivingroom: [
{
id: "talk", label: "Talk", passage: "brotherTalkLivingRoom",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhBackyard: [
{
id: "talk", label: "Talk", passage: "brotherTalkBackyard",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
]
}>>
/* ==========================================
CAREER SERVICES CLERK - Sandra Wilson
Career counselor at Town Hall
========================================== */
<<set setup.characterDefs.careerClerk = {
name: "Sandra Wilson",
firstName: "Sandra",
lastName: "Wilson",
birthYear: 1984,
gender: "female",
occupation: "Career Counselor",
location: "Town Hall - Career Services",
avatar: "assets/content/people/oldtown/townhall/sandraWilson.webp",
type: "npc",
color: "#8b5cf6",
info: "<p>A businesslike career counselor who works at the Town Hall Career Services office. She's efficient but somewhat impersonal in her approach.</p>",
status: "Career Counselor",
relationship: { status: "married", partnerLabel: "husband" },
maxLevels: { love: 0, friendship: 0, lust: 0, trust: 0 },
versionCaps: { friendship: 0, love: 0, trust: 0, lust: 0 }
}>>
<<set $characters.careerClerk = {
stats: { love: 0, loveLevel: 0, friendship: 0, friendshipLevel: 0, lust: 0, lustLevel: 0, trust: 0, trustLevel: 0 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>/* ==========================================
CHEF - Ruby's Diner
Kitchen staff, coworker
========================================== */
<<set setup.characterDefs.dinerChef = {
name: "James Holt",
firstName: "James",
lastName: "Holt",
birthYear: 1988,
gender: "male",
occupation: "Chef",
location: "Ruby's Diner",
avatar: "assets/content/people/oldtown/rubysdiner/jamesHolt.webp",
type: "npc",
color: "#dc2626",
info: "<p>Chef at Ruby's Diner. He runs the kitchen and keeps the place running during service.</p>",
status: "Chef",
relationship: { status: "married", partnerLabel: "wife", hasChildren: true },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 100, love: 0, trust: 30, lust: 0 }
}>>
<<set $characters.dinerChef = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.dinerChef = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 11, location: "dinerRubysKitchen", status: "available" },
{ hour: 14, location: "dinerRubysStorage", status: "available" },
{ hour: 15, location: "dinerRubysKitchen", status: "available" },
{ hour: 18, location: "dinerRubys", status: "available" },
{ hour: 19, location: "dinerRubysKitchen", status: "available" },
{ hour: 21, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 11, location: "dinerRubysKitchen", status: "available" },
{ hour: 14, location: "dinerRubysStorage", status: "available" },
{ hour: 15, location: "dinerRubysKitchen", status: "available" },
{ hour: 18, location: "dinerRubys", status: "available" },
{ hour: 19, location: "dinerRubysKitchen", status: "available" },
{ hour: 21, location: null, status: "unavailable" }
]
}>>
/* ACTIONS */
<<set setup.characterActions.dinerChef = {
dinerRubys: [
{
id: "talk", label: "Talk", passage: "jamesTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
}
],
dinerRubysKitchen: [
{
id: "talk", label: "Talk", passage: "jamesTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
}
],
dinerRubysStorage: [
{
id: "talk", label: "Talk", passage: "jamesTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
}
]
}>>/* ==========================================
CLERK - Ruby's Diner (front register)
30s, owner's right hand, kind and helpful
========================================== */
<<set setup.characterDefs.dinerClerk = {
name: "Tom Reese",
firstName: "Tom",
lastName: "Reese",
birthYear: 1992,
gender: "male",
occupation: "Front Clerk",
location: "Ruby's Diner",
avatar: "assets/content/people/oldtown/rubysdiner/tomReese.webp",
type: "npc",
color: "#0d9488",
info: "<p>Works the register at Ruby's Diner. He's been here for years and is the owner's right hand. Friendly and helpful.</p>",
status: "Clerk",
relationship: { status: "serious", partnerLabel: "girlfriend" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 100, love: 0, trust: 30, lust: 0 }
}>>
<<set $characters.dinerClerk = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.dinerClerk = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 10, location: "dinerRubys", status: "available" },
{ hour: 13, location: "dinerRubysStorage", status: "available" },
{ hour: 14, location: "dinerRubys", status: "available" },
{ hour: 18, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 10, location: "dinerRubys", status: "available" },
{ hour: 13, location: "dinerRubysStorage", status: "available" },
{ hour: 14, location: "dinerRubys", status: "available" },
{ hour: 18, location: null, status: "unavailable" }
]
}>>
/* ACTIONS */
<<set setup.characterActions.dinerClerk = {
dinerRubys: [
{
id: "talk", label: "Talk", passage: "tomTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, showWhenLocked: false, dailyLimit: true, minPlayerEnergy: 5
}
],
dinerRubysStorage: [
{
id: "talk", label: "Talk", passage: "tomTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, showWhenLocked: false, dailyLimit: true, minPlayerEnergy: 5
}
]
}>>/* charId: coffeeBaristaLeo Leo Harper, The Daily Grind barista */
<<set setup.characterDefs.coffeeBaristaLeo = {
firstName: "Leo",
lastName: "Harper",
birthYear: 2002,
gender: "male",
occupation: "Barista",
location: "The Daily Grind",
avatar: "assets/content/people/oldtown/coffeeshop/leoHarper.webp",
type: "npc",
color: "#78350f",
info: "<p>Works afternoon and evening shifts at The Daily Grind in Old Town.</p>",
status: "Barista",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 90, lust: 0 }
}>>
<<set $characters.coffeeBaristaLeo = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.coffeeBaristaLeo = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 12, location: "shopCoffeeOldtown", status: "available" },
{ hour: 20, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 11, location: "shopCoffeeOldtown", status: "available" },
{ hour: 20, location: null, status: "unavailable" }
]
}>>/* ==========================================
DISHWASHER - Ruby's Diner
Kitchen staff, coworker
========================================== */
<<set setup.characterDefs.dinerDishwasher = {
name: "Mike Torres",
firstName: "Mike",
lastName: "Torres",
birthYear: 1995,
gender: "male",
occupation: "Dishwasher",
location: "Ruby's Diner",
avatar: "assets/content/people/oldtown/rubysdiner/mikeTorres.webp",
type: "npc",
color: "#0ea5e9",
info: "<p>Dishwasher at Ruby's Diner. He works in the back and keeps the plates moving.</p>",
status: "Dishwasher",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 100, love: 0, trust: 30, lust: 0 }
}>>
<<set $characters.dinerDishwasher = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.dinerDishwasher = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 7, location: "dinerRubysKitchen", status: "available" },
{ hour: 10, location: "dinerRubysStorage", status: "available" },
{ hour: 11, location: "dinerRubysKitchen", status: "available" },
{ hour: 15, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 7, location: "dinerRubysKitchen", status: "available" },
{ hour: 10, location: "dinerRubysStorage", status: "available" },
{ hour: 11, location: "dinerRubysKitchen", status: "available" },
{ hour: 15, location: null, status: "unavailable" }
]
}>>
/* ACTIONS */
<<set setup.characterActions.dinerDishwasher = {
dinerRubysKitchen: [
{
id: "talk", label: "Talk", passage: "mikeTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
}
],
dinerRubysStorage: [
{
id: "talk", label: "Talk", passage: "mikeTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
}
]
}>>/* ==========================================
FATHER - Robert Taylor
Complete Character Definition
========================================== */
/* CHARACTER CONFIG (setup - survives version updates) */
<<set setup.characterDefs.father = {
maxLevels: { friendship: 2, love: 1, trust: 1, lust: 1 },
versionCaps: { friendship: 100, love: 20, trust: 50, lust: 0 }
}>>
/* CHARACTER DATA */
<<set $characters.father = {
firstName: "Robert",
lastName: "Taylor",
birthYear: 1979,
gender: "male",
occupation: "Software Engineer",
location: "Home, Study Room",
avatar: "assets/content/people/family/father/father.webp",
type: "npc",
color: "#3b82f6",
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
status: "Father",
firstMet: "Birth",
info: "<p>Your father is a software engineer who works long hours. He enjoys working on cars in the garage (his escape from screens) and watching sports.</p>",
known: true,
currentLocation: null,
currentStatus: null,
relationship: { status: "married", partnerLabel: "wife" }
}>>
/* SCHEDULE - Where is he at different times */
/* Location IDs match Navigation[INIT].twee */
/* preWork: Before work starts (settling into new home) */
/* postWork: After work starts (normal work routine) */
<<set setup.schedules.father = {
preWork: {
weekday: [
{ hour: 0, location: "fhParentsRoom", status: "sleeping" },
{ hour: 6, location: "fhParentsBath", status: "showering" }, /* Shower 6:00-6:20 (20 min) */
{ hour: 6, minute: 20, location: "fhParentsRoom", status: "available" }, /* Getting ready */
{ hour: 7, location: "fhKitchen", status: "available" }, /* Family Breakfast 7:00-7:30 */
{ hour: 7, minute: 30, location: "fhLivingroom", status: "available" },
{ hour: 10, location: "fhGarage", status: "available", meetup: true }, /* Garage Project */
{ hour: 12, minute: 30, location: "fhKitchen", status: "available" }, /* Lunch 12:30-13:00 */
{ hour: 13, location: "fhBackyard", status: "available" },
{ hour: 14, location: "fhGarage", status: "available", meetup: true }, /* Garage Afternoon */
{ hour: 17, location: "fhGarage", status: "available" },
{ hour: 18, location: "fhKitchen", status: "available" }, /* Family Dinner 18:30-19:30 */
{ hour: 19, minute: 30, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 23, location: "fhParentsRoom", status: "available" }
],
weekend: [
{ hour: 0, location: "fhParentsRoom", status: "sleeping" },
{ hour: 7, location: "fhParentsBath", status: "showering" }, /* Shower 7:00-7:20 (20 min) */
{ hour: 7, minute: 20, location: "fhParentsRoom", status: "available" }, /* Getting ready */
{ hour: 8, minute: 30, location: "fhKitchen", status: "available" }, /* Family Breakfast 8:30-9:30 */
{ hour: 9, minute: 30, location: "fhLivingroom", status: "available" },
{ hour: 11, location: "fhGarage", status: "available", meetup: true }, /* Weekend Garage */
{ hour: 12, minute: 30, location: "fhKitchen", status: "available" }, /* Family Lunch 12:30-13:30 */
{ hour: 13, minute: 30, location: "fhBackyard", status: "available" },
{ hour: 14, location: "fhGarage", status: "available", meetup: true }, /* Weekend Garage */
{ hour: 17, location: "fhLivingroom", status: "available" },
{ hour: 18, location: "fhKitchen", status: "available" }, /* Family Dinner 18:30-19:30 */
{ hour: 19, minute: 30, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 23, location: "fhParentsRoom", status: "available" }
]
},
postWork: {
weekday: [
{ hour: 0, location: "fhParentsRoom", status: "sleeping" },
{ hour: 6, location: "fhParentsBath", status: "showering" }, /* Shower 6:00-6:20 (20 min) */
{ hour: 6, minute: 20, location: "fhParentsRoom", status: "available" }, /* Getting ready */
{ hour: 7, location: "fhKitchen", status: "available" }, /* Family Breakfast 7:00-7:30 */
{ hour: 8, location: "work", status: "busy" }, /* At work 8:00-18:00 */
{ hour: 18, location: "fhKitchen", status: "available" }, /* Family Dinner 18:30-19:30 */
{ hour: 19, minute: 30, location: "fhGarage", status: "available", meetup: true }, /* Decompress after work */
{ hour: 20, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 23, location: "fhParentsRoom", status: "available" }
],
weekend: [
{ hour: 0, location: "fhParentsRoom", status: "sleeping" },
{ hour: 7, location: "fhParentsBath", status: "showering" }, /* Shower 7:00-7:20 (20 min) */
{ hour: 7, minute: 20, location: "fhParentsRoom", status: "available" }, /* Getting ready */
{ hour: 8, minute: 30, location: "fhKitchen", status: "available" }, /* Family Breakfast 8:30-9:30 */
{ hour: 9, minute: 30, location: "fhLivingroom", status: "available" },
{ hour: 11, location: "fhGarage", status: "available", meetup: true }, /* Weekend Project */
{ hour: 12, minute: 30, location: "fhKitchen", status: "available" }, /* Family Lunch 12:30-13:30 */
{ hour: 16, location: "fhLivingroom", status: "available" },
{ hour: 18, location: "fhKitchen", status: "available" }, /* Family Dinner 18:30-19:30 */
{ hour: 19, minute: 30, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 23, location: "fhParentsRoom", status: "available" }
]
}
}>>
/* ACTIONS - What can you do with him at each location */
/* Uses setup.* to avoid save file bloat */
<<set setup.characterActions.father = {
fhKitchen: [
{
id: "talk", label: "Talk", passage: "fatherTalkKitchen",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhLivingroom: [
{
id: "talk", label: "Talk", passage: "fatherTalkLivingRoom",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhBackyard: [
{
id: "talk", label: "Talk", passage: "fatherTalkBackyard",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhGarage: [
{
id: "vinceDay3FamilyFather",
label: "Ask for a minute",
passage: "quest_vince_day3_family_father",
tags: [],
requirements: { flag: "vinceDay3FamilyOpen", flagNot: "vinceDay3FamilyFatherDone" },
dailyLimit: false, minPlayerEnergy: 5, showWhenLocked: false, questButton: true
},
{
id: "talk", label: "Talk", passage: "fatherTalkGarage",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
]
}>>
/* ==========================================
VINCE'S GIRLFRIEND - Ruby's Diner (visitor)
40s, confident, comfortable in her position
Dialog id: <<dialog "dianaRuby">>
========================================== */
<<set setup.characterDefs.dianaRuby = {
name: "Diana Hayes",
firstName: "Diana",
lastName: "Hayes",
birthYear: 1983,
gender: "female",
occupation: "",
location: "Ruby's Diner (visitor)",
avatar: "assets/content/people/oldtown/rubysdiner/dianaHayes.webp",
type: "npc",
color: "#dc2626",
info: "<p>Vince's girlfriend. She shows up at the diner occasionally. Always dressed well, always comfortable like she owns the place too.</p>",
status: "Vince's girlfriend",
relationship: { status: "dating", partnerLabel: "boyfriend" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 0, love: 0, trust: 0, lust: 0 }
}>>
<<set $characters.dianaRuby = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>/* charId: gymRegularKayla Kayla Morgan, morning gym regular */
<<set setup.characterDefs.gymRegularKayla = {
firstName: "Kayla",
lastName: "Morgan",
birthYear: 2002,
gender: "female",
occupation: "Gym Regular",
location: "Iron Works Gym",
avatar: "assets/content/people/downtown/gym/kaylaMorgan.webp",
type: "npc",
color: "#0891b2",
info: "<p>A regular at Iron Works who favors morning cardio.</p>",
status: "Regular",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 90, lust: 0 }
}>>
<<set $characters.gymRegularKayla = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.gymRegularKayla = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 7, location: "gym", status: "available" },
{ hour: 11, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 8, location: "gym", status: "available" },
{ hour: 12, location: null, status: "unavailable" }
]
}>>
/* charId: gymReceptionNora Nora Carter, Iron Works front desk */
<<set setup.characterDefs.gymReceptionNora = {
firstName: "Nora",
lastName: "Carter",
birthYear: 1999,
gender: "female",
occupation: "Gym Receptionist",
location: "Iron Works Gym",
avatar: "assets/content/people/downtown/gym/noraCarter.webp",
type: "npc",
color: "#a855f7",
info: "<p>The front desk receptionist at Iron Works Gym. Friendly, organized, and always tracking new sign-ups.</p>",
status: "Reception",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 90, lust: 0 }
}>>
<<set $characters.gymReceptionNora = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.gymReceptionNora = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 8, location: "gym", status: "reception" },
{ hour: 12, location: null, status: "break" },
{ hour: 13, location: "gym", status: "reception" },
{ hour: 17, location: "gym", status: "trainer" },
{ hour: 19, location: "gym", status: "reception" },
{ hour: 22, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 8, location: "gym", status: "reception" },
{ hour: 12, location: null, status: "break" },
{ hour: 13, location: "gym", status: "reception" },
{ hour: 17, location: "gym", status: "trainer" },
{ hour: 19, location: "gym", status: "reception" },
{ hour: 22, location: null, status: "unavailable" }
]
}>>
<<set setup.characterActions.gymReceptionNora = {
gym: [
{
id: "talk", label: "Talk", passage: "gymReceptionNoraTalk",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 2
}
]
}>>
/* charId: gymTrainerNick Nick Santos, Iron Works Gym trainer */
<<set setup.characterDefs.gymTrainerNick = {
firstName: "Nick",
lastName: "Santos",
birthYear: 1994,
gender: "male",
occupation: "Personal Trainer",
location: "Iron Works Gym",
avatar: "assets/content/people/downtown/gym/nickSantos.webp",
type: "npc",
color: "#ea580c",
info: "<p>A professional trainer at Iron Works Gym. Usually around for early birds and after-work sessions.</p>",
status: "Trainer",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 90, lust: 0 }
}>>
<<set $characters.gymTrainerNick = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.gymTrainerNick = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 8, location: "gym", status: "trainer" },
{ hour: 12, location: "gym", status: "reception" },
{ hour: 13, location: "gym", status: "trainer" },
{ hour: 17, location: null, status: "break" },
{ hour: 18, location: "gym", status: "trainer" },
{ hour: 22, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 8, location: "gym", status: "trainer" },
{ hour: 12, location: "gym", status: "reception" },
{ hour: 13, location: "gym", status: "trainer" },
{ hour: 17, location: null, status: "break" },
{ hour: 18, location: "gym", status: "trainer" },
{ hour: 22, location: null, status: "unavailable" }
]
}>>
/* charId: gymRegularTyler Tyler Reed, afternoon weights regular */
<<set setup.characterDefs.gymRegularTyler = {
firstName: "Tyler",
lastName: "Reed",
birthYear: 2001,
gender: "male",
occupation: "Gym Regular",
location: "Iron Works Gym",
avatar: "assets/content/people/downtown/gym/tylerReed.webp",
type: "npc",
color: "#d97706",
info: "<p>A regular at Iron Works who tends to lift in the afternoon.</p>",
status: "Regular",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 90, lust: 0 }
}>>
<<set $characters.gymRegularTyler = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.gymRegularTyler = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 14, location: "gym", status: "available" },
{ hour: 19, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 13, location: "gym", status: "available" },
{ hour: 19, location: null, status: "unavailable" }
]
}>>
/* ==========================================
PARK RUNNER - Lily Morgan
Regular runner at Sunset Park. First met on the bench.
25-28, friendly, sporty.
========================================= */
<<set setup.characterDefs.parkRunnerLily = {
name: "Lily Morgan",
firstName: "Lily",
lastName: "Morgan",
birthYear: 1998,
gender: "female",
occupation: "Fitness Enthusiast",
location: "Sunset Park",
avatar: "assets/content/people/maplewood/sunsetPark/lilyMorgan.webp",
type: "npc",
color: "#06b6d4",
info: "<p>A friendly woman you met at Sunset Park. She runs there regularly and pointed you to the mall's sports store. Approachable and welcoming.</p>",
status: "Park Regular",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 0, lust: 0 }
}>>
<<set $characters.parkRunnerLily = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.parkRunnerLily = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 7, location: "sunsetPark", status: "jogging" },
{ hour: 9, location: null, status: "unavailable" },
{ hour: 17, location: "sunsetPark", status: "jogging" },
{ hour: 18, location: "gym", status: "training" },
{ hour: 20, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 8, location: "sunsetPark", status: "jogging" },
{ hour: 12, location: null, status: "unavailable" },
{ hour: 16, location: "sunsetPark", status: "jogging" },
{ hour: 17, location: "gym", status: "training" },
{ hour: 19, location: null, status: "unavailable" }
]
}>>
<<set setup.characterActions.parkRunnerLily = {
sunsetPark: [
{
id: "talk", label: "Talk", passage: "parkRunnerLilyTalk",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "jogTogether", label: "Jog together", passage: "parkRunnerLilyJogTogether",
tags: [], requirements: { flag: "lilyJogUnlocked", outfit: "sporty 3", minStat: "energy 25", friendshipLevel: 1, friendship: 20 }, hideWhenFlagLocked: true, dailyLimit: true
},
]
}>>
/* ==========================================
LUXURY BOUTIQUE CLERK - Metro Mall (upper floor)
Scene-only NPC; <<dialog "mallClerk">> in Diana arc.
========================================== */
<<set setup.characterDefs.mallClerk = {
name: "Sienna Park",
firstName: "Sienna",
lastName: "Park",
birthYear: 1992,
gender: "female",
occupation: "Sales associate",
location: "Metro Shopping Center",
avatar: "assets/content/people/downtown/mall/luxuryStore/SiennaPark.webp",
type: "npc",
color: "#64748b",
info: "<p>A polished sales associate at a high-end boutique in the mall. Pleasant on the surface, exacting underneath.</p>",
status: "Sales associate",
relationship: { status: "single" },
maxLevels: { love: 0, friendship: 0, lust: 0, trust: 0 },
versionCaps: { friendship: 0, love: 0, trust: 0, lust: 0 }
}>>
<<set $characters.mallClerk = {
stats: { love: 0, loveLevel: 0, friendship: 0, friendshipLevel: 0, lust: 0, lustLevel: 0, trust: 0, trustLevel: 0 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>/* ==========================================
MANAGER / OWNER - Ruby's Diner
Owner, 40s, demeaning, clingy, slightly creepy
========================================== */
<<set setup.characterDefs.dinerManager = {
name: "Vince Ruby",
firstName: "Vince",
lastName: "Ruby",
birthYear: 1982,
gender: "male",
occupation: "Owner / Manager",
location: "Ruby's Diner",
avatar: "assets/content/people/oldtown/rubysdiner/vinceRuby.webp",
type: "npc",
color: "#b45309",
info: "<p>Owner and manager of Ruby's Diner. He runs the place with an iron hand and a habit of looking a little too long.</p>",
status: "Manager",
relationship: { status: "divorced", partnerLabel: "ex-wife" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 100, love: 0, trust: 30, lust: 0 }
}>>
<<set $characters.dinerManager = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.dinerManager = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 10, location: "dinerRubysManagerOffice", status: "available" },
{ hour: 12, location: "dinerRubys", status: "available" },
{ hour: 14, location: "dinerRubysManagerOffice", status: "available" },
{ hour: 16, location: "dinerRubysKitchen", status: "available" },
{ hour: 17, location: "dinerRubys", status: "available" },
{ hour: 19, location: "dinerRubysManagerOffice", status: "available" },
{ hour: 22, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 10, location: "dinerRubysManagerOffice", status: "available" },
{ hour: 12, location: "dinerRubys", status: "available" },
{ hour: 14, location: "dinerRubysManagerOffice", status: "available" },
{ hour: 16, location: "dinerRubysKitchen", status: "available" },
{ hour: 17, location: "dinerRubys", status: "available" },
{ hour: 19, location: "dinerRubysManagerOffice", status: "available" },
{ hour: 22, location: null, status: "unavailable" }
],
sunday: [
{ hour: 0, location: null, status: "unavailable" }
]
}>>
/* ACTIONS */
<<set setup.characterActions.dinerManager = {
dinerRubys: [
{
id: "talk", label: "Talk", passage: "vinceTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, showWhenLocked: false, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "bossPromotionTalk", label: "Discuss Promotion", passage: "jobBossOfficeCall_ruby",
tags: [], requirements: { firstWorkDayEventShown: true, promotionEligible: true }, showWhenLocked: true, dailyLimit: false, minPlayerEnergy: 0
}
],
dinerRubysManagerOffice: [
{
id: "talk", label: "Talk", passage: "vinceTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, showWhenLocked: false, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "bossPromotionTalk", label: "Discuss Promotion", passage: "jobBossOfficeCall_ruby",
tags: [], requirements: { firstWorkDayEventShown: true, promotionEligible: true }, showWhenLocked: true, dailyLimit: false, minPlayerEnergy: 0
}
],
dinerRubysKitchen: [
{
id: "talk", label: "Talk", passage: "vinceTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, showWhenLocked: false, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "bossPromotionTalk", label: "Discuss Promotion", passage: "jobBossOfficeCall_ruby",
tags: [], requirements: { firstWorkDayEventShown: true, promotionEligible: true }, showWhenLocked: true, dailyLimit: false, minPlayerEnergy: 0
}
]
}>>/* ==========================================
SHOP CLERK - Marcus
Friendly corner shop employee
========================================== */
<<set setup.characterDefs.shopClerkMarcus = {
name: "Marcus",
firstName: "Marcus",
lastName: "Johnson",
birthYear: 1998,
gender: "male",
occupation: "Shop Clerk",
location: "Quick Mart",
avatar: "assets/content/people/maplewood/cornershop/marcus.webp",
type: "npc",
color: "#f59e0b",
info: "<p>A friendly young man who works at the Quick Mart convenience store. He's always helpful and welcoming to newcomers in the neighborhood.</p>",
status: "Shop Clerk",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 0, lust: 0 }
}>>
<<set $characters.shopClerkMarcus = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.shopClerkMarcus = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 9, location: "storeCorner", status: "available" },
{ hour: 22, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 10, location: "storeCorner", status: "available" },
{ hour: 20, location: null, status: "unavailable" }
]
}>>
<<set setup.characterActions.shopClerkMarcus = {
storeCorner: [
{
id: "talk", label: "Talk", passage: "shopClerkTalk",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
},
]
}>>
/* ==========================================
MOTHER - Sarah Taylor
Complete Character Definition
========================================== */
/* CHARACTER CONFIG (setup - survives version updates) */
<<set setup.characterDefs.mother = {
maxLevels: { friendship: 2, love: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 100, love: 30, trust: 30, lust: 20 }
}>>
/* CHARACTER DATA */
<<set $characters.mother = {
name: "Sarah",
firstName: "Sarah",
lastName: "Taylor",
birthYear: 1982,
gender: "female",
occupation: "House Wife",
location: "Home, Living Room",
avatar: "assets/content/people/family/mother/mother.webp",
type: "npc",
color: "#8b5cf6",
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
status: "Mother",
firstMet: "Birth",
info: "<p>Your loving mother who always cares about your well-being. She enjoys cooking, jogging, and reading romance novels.</p>",
known: true,
currentLocation: null,
currentStatus: null,
relationship: { status: "married", partnerLabel: "husband" }
}>>
/* SCHEDULE - Where is she at different times */
/* Location IDs match Navigation[INIT].twee */
<<set setup.schedules.mother = {
weekday: [
{ hour: 0, location: "fhParentsRoom", status: "sleeping" },
{ hour: 6, minute: 20, location: "fhParentsBath", status: "showering" },
{ hour: 6, minute: 50, location: "fhParentsRoom", status: "available" },
{ hour: 7, location: "fhKitchen", status: "available" },
{ hour: 7, minute: 30, location: "fhLivingroom", status: "available" },
{ hour: 9, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 12, minute: 30, location: "fhKitchen", status: "available" },
{ hour: 14, location: "fhKitchen", status: "available", meetup: true },
{ hour: 18, location: "fhKitchen", status: "available" },
{ hour: 19, minute: 30, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 23, location: "fhParentsRoom", status: "available" }
],
weekend: [
{ hour: 0, location: "fhParentsRoom", status: "sleeping" },
{ hour: 7, minute: 20, location: "fhParentsBath", status: "showering" },
{ hour: 7, minute: 50, location: "fhParentsRoom", status: "available" },
{ hour: 8, minute: 30, location: "fhKitchen", status: "available" },
{ hour: 9, minute: 30, location: "fhLivingroom", status: "available" },
{ hour: 10, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 12, minute: 30, location: "fhKitchen", status: "available" },
{ hour: 14, location: "fhKitchen", status: "available", meetup: true },
{ hour: 18, location: "fhKitchen", status: "available" },
{ hour: 19, minute: 30, location: "fhLivingroom", status: "available", meetup: true },
{ hour: 23, location: "fhParentsRoom", status: "available" }
]
}>>
/* ACTIONS - What can you do with her at each location */
/* Uses setup.* to avoid save file bloat */
<<set setup.characterActions.mother = {
fhKitchen: [
{
id: "vinceDay3FamilyMom",
label: "Something happened at work",
passage: "quest_vince_day3_family_mother",
tags: [],
requirements: { flag: "vinceDay3FamilyOpen", flagNot: "vinceDay3FamilyMomDone" },
dailyLimit: false, minPlayerEnergy: 5, showWhenLocked: false, questButton: true
},
{
id: "talk", label: "Talk", passage: "motherTalkKitchen",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhLivingroom: [
{
id: "talk", label: "Talk", passage: "motherTalkLivingRoom",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhBackyard: [
{
id: "talk", label: "Talk", passage: "motherTalkBackyard",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
],
fhParentsRoom: [
{
id: "talk", label: "Talk", passage: "motherTalkParentsRoom",
tags: [], requirements: {}, dailyLimit: true, minPlayerEnergy: 5
}
]
}>>
/* charId: neighborElena Elena Harrison, Mia's mother [future: Mia's house] */
<<set setup.characterDefs.neighborElena = {
firstName: "Elena",
lastName: "Harrison",
birthYear: 1982,
gender: "female",
occupation: "Homemaker",
location: "Maplewood",
avatar: "assets/content/people/maplewood/harrisons/elenaHarrison.webp",
type: "npc",
color: "#c026d3",
info: "<p>Mia's mother. Warm and welcoming when you eventually meet at the Harrisons' place.</p>",
status: "Mia's mom",
relationship: { status: "married", partnerLabel: "husband" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 90, lust: 0 }
}>>
<<set $characters.neighborElena = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.neighborElena = {
weekday: [
{ hour: 0, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" }
]
}>>
/* charId: neighborEthan Ethan Clarke, Maplewood / Ethan romantic arc */
<<set setup.characterDefs.neighborEthan = {
firstName: "Ethan",
lastName: "Clarke",
birthYear: 2005,
gender: "male",
occupation: "Neighbor",
location: "Maplewood",
avatar: "assets/content/people/maplewood/neighbors/ethanClarke.webp",
type: "npc",
color: "#059669",
info: "<p>A guy from the neighborhood you might run into while exploring Maplewood.</p>",
status: "Neighbor",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 90, trust: 90, lust: 0 }
}>>
<<set $characters.neighborEthan = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.neighborEthan = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 14, location: "maplewood", status: "available" },
{ hour: 19, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 13, location: "maplewood", status: "available" },
{ hour: 20, location: null, status: "unavailable" }
]
}>>/* charId: neighborMia Mia Harrison, Maplewood neighbor (guide / Mia arc) */
<<set setup.characterDefs.neighborMia = {
firstName: "Mia",
lastName: "Harrison",
birthYear: 2005,
gender: "female",
occupation: "Neighbor",
location: "Maplewood",
avatar: "assets/content/people/maplewood/harrisons/miaHarrison.webp",
type: "npc",
color: "#e11d48",
info: "<p>Your outgoing neighbor in Maplewood. Friendly and quick to point you toward places and people worth knowing.</p>",
status: "Neighbor",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 0, lust: 0 }
}>>
<<set $characters.neighborMia = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.neighborMia = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 11, location: "maplewood", status: "available" },
{ hour: 12, location: null, status: "unavailable" },
{ hour: 17, location: "sunsetPark", status: "hanging_out" },
{ hour: 18, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 10, location: "maplewood", status: "available" },
{ hour: 11, location: null, status: "unavailable" },
{ hour: 16, location: "sunsetPark", status: "hanging_out" },
{ hour: 17, location: null, status: "unavailable" }
]
}>>
/* charId: neighborRobert Robert Harrison, Mia's father [future: Mia's house] */
<<set setup.characterDefs.neighborRobert = {
firstName: "Robert",
lastName: "Harrison",
birthYear: 1980,
gender: "male",
occupation: "Professional",
location: "Maplewood",
avatar: "assets/content/people/maplewood/harrisons/robertHarrison.webp",
type: "npc",
color: "#2563eb",
info: "<p>Mia's father. Easygoing and put-together when you meet him at home.</p>",
status: "Mia's dad",
relationship: { status: "married", partnerLabel: "wife" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 90, love: 0, trust: 90, lust: 0 }
}>>
<<set $characters.neighborRobert = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.neighborRobert = {
weekday: [
{ hour: 0, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" }
]
}>>
/* ==========================================
ST. LUKE'S HOSPITAL staff (generic RN)
Dialog id: nurse (<<dialog "nurse">> in hospitalAdmission etc.)
Reuse: reference setup.characterDefs.nurse / char id "nurse"
========================================== */
<<set setup.characterDefs.nurse = {
firstName: "Rachel",
lastName: "Brooks",
birthYear: 1992,
gender: "female",
occupation: "Registered Nurse",
location: "St. Luke's Hospital",
avatar: "assets/content/people/oldtown/hospital/nurseRachelBrooks.webp",
type: "npc",
color: "#0d9488",
info: "<p>A calm, professional nurse at St. Luke's. She handles admissions and recovery checks; you may see her again on future hospital scenes.</p>",
status: "RN, St. Luke's",
relationship: { status: "single" }
}>>
<<set $characters.nurse = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: true,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.nurse = {
weekday: [
{ hour: 0, location: "hospital", status: "working" }
],
weekend: [
{ hour: 0, location: "hospital", status: "working" }
]
}>>
/* ==========================================
PLAYER - You
Player Character Definition & Initialization
V1 Final Stat System
========================================== */
/* ==========================================
1. BASIC INFO & APPEARANCE
========================================== */
<<set $player = {
firstName: "Alex",
lastName: "Taylor",
gender: "female",
eyeColor: "Black",
hairColor: "Black",
hairLength: "Short",
hairStyle: "Straight",
bustSize: "A",
hipSize: "Slim"
}>>
/* DYNAMIC APPEARANCE */
<<set $appearance = {
hairLengthCm: 45,
tanLevel: 0,
makeupLevel: 0,
hairMessiness: 0,
hairCombed: 1,
nailPolish: "none",
hairStyle: "loose",
bodyHair: {
legs: 0,
pubic: 0,
armpits: 0
},
/* Hair / face / dental care (0–100); decays without routine, rises with actions; affects beauty/looks */
hairCare: 100,
faceCare: 100,
dentalCare: 100
}>>
/* PLASTIC SURGERY */
<<set $plasticSurgery = {
lipsSize: 50,
eyeSize: 50,
eyelashes: 50
}>>
/* BODY CHANGES */
<<set $bodyChanges = {
bustGrowth: 0,
hipGrowth: 0,
pregnancyWeek: 0,
lactationActive: false
}>>
/* BODY MEASUREMENTS */
<<set $body = {
height: 170,
weight: 65,
bust: 90,
waist: 70,
hips: 95,
muscleMass: 40,
bodyFat: 22,
appeal: 50,
bmi: 22.5,
bodyType: "Normal"
}>>
/* WEIGHT TRACKING (for weekly weigh-in) */
<<set $body.previousWeight = $body.weight>>
<<set $body.previousBMI = $body.bmi>>
/* ==========================================
2. DAILY STATS
========================================== */
<<set $energy = 100>>
<<set $energyMax = 100>>
<<set $mood = 100>>
<<set $hygiene = 100>>
<<set $health = 100>>
<<set $stress = 0>>
<<set $arousal = 0>>
/* MONEY SYSTEM */
<<set $moneyEarn = 0>> /* Total cash earned */
<<set $moneySpend = 0>> /* Total cash spent */
<<set $cashBalance = 0>> /* Current cash: moneyEarn - moneySpend */
<<set $bankDeposit = 0>> /* Total deposited to bank */
<<set $bankSpend = 0>> /* Total spent via card */
<<set $bankWithdraw = 0>> /* Total withdrawn from bank */
<<set $bankBalance = 0>> /* Current bank: bankDeposit - bankSpend - bankWithdraw */
/* BASIC NEEDS (Optional - Enabled from Settings) */
<<set $hunger = 0>>
<<set $thirst = 0>>
<<set $bladder = 0>>
/* ==========================================
3. MENTAL STATS
========================================== */
<<set $intelligence = 0>>
<<set $focus = 0>>
<<set $creativity = 0>>
<<set $willpower = 0>>
/* ==========================================
4. SOCIAL STATS
========================================== */
<<set $charisma = 0>>
<<set $confidence = 0>>
/* ==========================================
5. PHYSICAL STATS (FITNESS)
========================================== */
<<set $upperBody = 0>>
<<set $core = 0>>
<<set $lowerBody = 0>>
<<set $cardio = 0>>
<<set $fitness = 0>>
/* ==========================================
6. LOOKS STATS
========================================== */
<<set $beauty = 0>>
<<set $clothingScore = 0>>
<<set $looks = 0>>
/* ==========================================
7. SPECIAL STATS
========================================== */
<<set $corruption = 0>>
<<set $painTolerance = 0>>
<<set $exhibitionism = 0>>
<<set $obedience = 0>>
/* ==========================================
8. SEXUAL STATS & SKILLS
========================================== */
<<set $sexual = {
sensitivity: 0,
experience: 0,
virginity: {
vaginal: { intact: true, takenBy: null, takenByName: null, date: null },
anal: { intact: true, takenBy: null, takenByName: null, date: null },
oral: { intact: true, takenBy: null, takenByName: null, date: null }
},
stretch: {
vaginal: 0,
anal: 0
},
counts: {
vaginal: 0,
anal: 0,
oral: 0,
handjob: 0,
masturbation: 0,
gangbang: 0,
public: 0
},
skills: {
oral: 0,
deepthroat: 0,
handjob: 0,
riding: 0,
anal: 0,
foreplay: 0,
seduction: 0,
teasing: 0
},
totalPartners: 0,
photosTaken: 0,
videosTaken: 0
}>>
/* ==========================================
9. SKILLS (Categorized)
========================================== */
<<set $skills = {
mental: {
research: 0,
problemSolving: 0,
analysis: 0
},
social: {
conversation: 0,
persuasion: 0,
networking: 0
},
physical: {
dance: 0,
running: 0,
yoga: 0,
basketball: 0,
volleyball: 0,
football: 0,
swimming: 0,
heels: 0
},
creative: {
art: 0,
music: 0,
writing: 0
},
technical: {
programming: 0,
hacking: 0,
electronics: 0,
gaming: 0
},
practical: {
cooking: 0,
cleaning: 0,
dishwashing: 0,
driving: 0,
finance: 0,
mechanics: 0,
gardening: 0,
makeup: 0
}
}>>
/* ==========================================
10. MULTIPLIERS (Comes from Prologue)
========================================== */
<<set $statMultipliers = {
intelligence: 1.0,
focus: 1.0,
creativity: 1.0,
willpower: 1.0,
charisma: 1.0,
fitness: 1.0,
upperBody: 1.0,
core: 1.0,
lowerBody: 1.0,
cardio: 1.0,
beauty: 1.0,
/* Adult/Special Stats */
corruption: 1.0,
obedience: 1.0,
sensitivity: 1.0,
painTolerance: 1.0,
exhibitionism: 1.0
}>>
<<set $skillMultipliers = {
mental: 1.0,
social: 1.0,
physical: 1.0,
creative: 1.0,
technical: 1.0,
practical: 1.0
}>>
/* ==========================================
11. REPUTATION (Zone Based)
========================================== */
<<set $reputation = {
home: 50,
campus: 0,
downtown: 0,
workplace: 0,
socialMedia: 0
}>>
/* ==========================================
12. JOURNAL & FIRSTS
========================================== */
<<set $firsts = {
firstKiss: null,
firstVaginal: null,
firstAnal: null,
firstOralGiven: null,
firstOralReceived: null,
firstThreesome: null,
firstPublic: null,
firstPregnancy: null,
firstPhoto: null,
firstMasturbation: null
}>>
/* ==========================================
13. CHARACTER DATA OBJECT
========================================== */
<<set $characters.player = {
name: "Alex",
lastname: "Taylor",
birthYear: 2007,
gender: "female",
occupation: "Unemployed",
location: "Home",
avatar: "assets/content/people/player/body/profile/playerProfile1-1.webp",
type: "player",
color: "#ec4899",
stats: {
love: 0,
friendship: 0,
lust: 0,
trust: 0
},
status: "Player",
firstMet: "Birth",
info: "<p>This is you.</p>",
known: true,
traits: []
}>>
/* ==========================================
14. DAILY TRACKING
========================================== */
<<set $dailyCalorieIntake = 0>>
<<set $dailyExercise = 0>>
<<set $basalMetabolicRate = 2000>>
/* ==========================================
15. SKILL USAGE TRACKING (for Decay System)
========================================== */
<<set $skillUsageTracking = {
mental: {},
social: {},
physical: {},
creative: {},
technical: {},
practical: {}
}>>
/* ==========================================
15. PROLOGUE SELECTIONS
========================================== */
<<set $prologueSelections = {
earlyYears: null,
childhood: null,
formative: null,
adolescent: null,
comingOfAge: null
}>>
/* ==========================================
16. INVENTORY & SHOPPING
========================================== */
<<set $inventory = []>> /* Player's inventory: [{id, quantity}, ...] */
<<set $shoppingCart = []>> /* Temporary cart: [{id, quantity, price}, ...] */
/* READING SYSTEM */
<<set $readProgress = {}>> /* { bookId: pagesRead } - partial progress */
<<set $readFinished = []>> /* [bookId, ...] - finished books (for My Books list) */
/* ==========================================
17. WARDROBE INITIALIZATION
========================================== */
<<set $wardrobe = {
owned: ["tshirt_white_owned", "jeans_blue_owned", "sneakers_flat_owned", "socks_white_owned", "panties_white_owned", "bra_sport_white_owned"],
outfits: [null, null, null, null, null],
equipped: {
top: "tshirt_white_owned",
bottom: "jeans_blue_owned",
shoes: "sneakers_flat_owned",
socks: "socks_white_owned",
panty: "panties_white_owned",
bra: "bra_sport_white_owned"
}
}>>
<<recalculateStats>>
/* ==========================================
TOWN HALL RECEPTIONIST
Information desk worker at Town Hall
========================================== */
<<set setup.characterDefs.townHallReceptionist = {
name: "Receptionist",
firstName: "Janet",
lastName: "Morrison",
birthYear: 1974,
gender: "female",
occupation: "Receptionist",
location: "Town Hall",
avatar: "assets/content/people/oldtown/townhall/janetMorrison.webp",
type: "npc",
color: "#6b7280",
info: "<p>A professional woman who works at the Town Hall information desk. She's been here for years and knows the building inside and out.</p>",
status: "Receptionist",
relationship: { status: "married", partnerLabel: "husband" },
maxLevels: { love: 0, friendship: 0, lust: 0, trust: 0 },
versionCaps: { friendship: 0, love: 0, trust: 0, lust: 0 }
}>>
<<set $characters.townHallReceptionist = {
stats: { love: 0, loveLevel: 0, friendship: 0, friendshipLevel: 0, lust: 0, lustLevel: 0, trust: 0, trustLevel: 0 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>/* ==========================================
WAITRESS - Ruby's Diner (1 of 3, female)
Front of house, coworker
========================================== */
<<set setup.characterDefs.dinerWaitress1 = {
name: "Emma Walsh",
firstName: "Emma",
lastName: "Walsh",
birthYear: 1999,
gender: "female",
occupation: "Waitress",
location: "Ruby's Diner",
avatar: "assets/content/people/oldtown/rubysdiner/emmaWalsh.webp",
type: "npc",
color: "#8b5cf6",
info: "<p>Waitress at Ruby's Diner. She's been there a while and knows the regulars.</p>",
status: "Waitress",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 100, love: 0, trust: 30, lust: 0 }
}>>
<<set $characters.dinerWaitress1 = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.dinerWaitress1 = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 10, location: "dinerRubys", status: "available" },
{ hour: 13, location: "dinerRubysStorage", status: "available" },
{ hour: 14, location: "dinerRubys", status: "available" },
{ hour: 16, location: "dinerRubysKitchen", status: "available" },
{ hour: 17, location: "dinerRubys", status: "available" },
{ hour: 18, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 10, location: "dinerRubys", status: "available" },
{ hour: 13, location: "dinerRubysStorage", status: "available" },
{ hour: 14, location: "dinerRubys", status: "available" },
{ hour: 16, location: "dinerRubysKitchen", status: "available" },
{ hour: 17, location: "dinerRubys", status: "available" },
{ hour: 18, location: null, status: "unavailable" }
]
}>>
/* ACTIONS */
<<set setup.characterActions.dinerWaitress1 = {
dinerRubys: [
{
id: "talk", label: "Talk", passage: "emmaTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "gossipDiana", label: "Vince's girlfriend", passage: "emmaTalkDinerRubys_dianaGossip",
tags: [], requirements: { firstWorkDayEventShown: true, flag: "dianaGossipUnlocked", flagNot: "emmaGossipDone" },
dailyLimit: false, minPlayerEnergy: 5, showWhenLocked: false
}
],
dinerRubysKitchen: [
{
id: "talk", label: "Talk", passage: "emmaTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "gossipDiana", label: "Vince's girlfriend", passage: "emmaTalkDinerRubys_dianaGossip",
tags: [], requirements: { firstWorkDayEventShown: true, flag: "dianaGossipUnlocked", flagNot: "emmaGossipDone" },
dailyLimit: false, minPlayerEnergy: 5, showWhenLocked: false
}
],
dinerRubysStorage: [
{
id: "talk", label: "Talk", passage: "emmaTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "gossipDiana", label: "Vince's girlfriend", passage: "emmaTalkDinerRubys_dianaGossip",
tags: [], requirements: { firstWorkDayEventShown: true, flag: "dianaGossipUnlocked", flagNot: "emmaGossipDone" },
dailyLimit: false, minPlayerEnergy: 5, showWhenLocked: false
}
]
}>>/* ==========================================
WAITER - Ruby's Diner (3 of 3, male)
Front of house, coworker
========================================== */
<<set setup.characterDefs.dinerWaitress3 = {
name: "Jake Rivera",
firstName: "Jake",
lastName: "Rivera",
birthYear: 1997,
gender: "male",
occupation: "Waiter",
location: "Ruby's Diner",
avatar: "assets/content/people/oldtown/rubysdiner/jakeRivera.webp",
type: "npc",
color: "#10b981",
info: "<p>Waiter at Ruby's Diner. He works the floor with the other waitstaff.</p>",
status: "Waiter",
relationship: { status: "single" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 100, love: 0, trust: 30, lust: 0 }
}>>
<<set $characters.dinerWaitress3 = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.dinerWaitress3 = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 14, location: "dinerRubys", status: "available" },
{ hour: 17, location: "dinerRubysStorage", status: "available" },
{ hour: 18, location: "dinerRubys", status: "available" },
{ hour: 22, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 14, location: "dinerRubys", status: "available" },
{ hour: 17, location: "dinerRubysStorage", status: "available" },
{ hour: 18, location: "dinerRubys", status: "available" },
{ hour: 22, location: null, status: "unavailable" }
]
}>>
/* ACTIONS */
<<set setup.characterActions.dinerWaitress3 = {
dinerRubys: [
{
id: "talk", label: "Talk", passage: "jakeTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
}
],
dinerRubysStorage: [
{
id: "talk", label: "Talk", passage: "jakeTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
}
]
}>>/* ==========================================
WAITRESS - Ruby's Diner (2 of 3, female)
Front of house, coworker
========================================== */
<<set setup.characterDefs.dinerWaitress2 = {
name: "Sofia Chen",
firstName: "Sofia",
lastName: "Chen",
birthYear: 2001,
gender: "female",
occupation: "Waitress",
location: "Ruby's Diner",
avatar: "assets/content/people/oldtown/rubysdiner/sofiaChen.webp",
type: "npc",
color: "#c026d3",
info: "<p>Waitress at Ruby's Diner. She usually works the morning shift.</p>",
status: "Waitress",
relationship: { status: "dating", partnerLabel: "boyfriend" },
maxLevels: { love: 1, friendship: 1, lust: 1, trust: 1 },
versionCaps: { friendship: 100, love: 0, trust: 30, lust: 0 }
}>>
<<set $characters.dinerWaitress2 = {
stats: {
love: 0, loveLevel: 1,
friendship: 0, friendshipLevel: 1,
lust: 0, lustLevel: 1,
trust: 0, trustLevel: 1
},
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.dinerWaitress2 = {
weekday: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 12, location: "dinerRubys", status: "available" },
{ hour: 15, location: "dinerRubysStorage", status: "available" },
{ hour: 16, location: "dinerRubys", status: "available" },
{ hour: 20, location: null, status: "unavailable" }
],
weekend: [
{ hour: 0, location: null, status: "unavailable" },
{ hour: 12, location: "dinerRubys", status: "available" },
{ hour: 15, location: "dinerRubysStorage", status: "available" },
{ hour: 16, location: "dinerRubys", status: "available" },
{ hour: 20, location: null, status: "unavailable" }
]
}>>
/* ACTIONS */
<<set setup.characterActions.dinerWaitress2 = {
dinerRubys: [
{
id: "talk", label: "Talk", passage: "sofiaTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "gossipDiana", label: "Vince's girlfriend", passage: "sofiaTalkDinerRubys_dianaGossip",
tags: [], requirements: { firstWorkDayEventShown: true, flag: "sofiaGossipUnlocked", flagNot: "sofiaGossipDone" },
dailyLimit: false, minPlayerEnergy: 5, showWhenLocked: false
}
],
dinerRubysStorage: [
{
id: "talk", label: "Talk", passage: "sofiaTalkDinerRubys",
tags: [], requirements: { firstWorkDayEventShown: true }, dailyLimit: true, minPlayerEnergy: 5
},
{
id: "gossipDiana", label: "Vince's girlfriend", passage: "sofiaTalkDinerRubys_dianaGossip",
tags: [], requirements: { firstWorkDayEventShown: true, flag: "sofiaGossipUnlocked", flagNot: "sofiaGossipDone" },
dailyLimit: false, minPlayerEnergy: 5, showWhenLocked: false
}
]
}>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<!-- Sidebar -->
<div class="prologue-sidebar">
<div class="prologue-sidebar-title">My Selections</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Early Years (0-5)</div>
<<if $prologueSelections.early>>
<div class="prologue-sidebar-item">
<div>
<div class="prologue-sidebar-item-text">$prologueSelections.early.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.early.effect</div>
</div>
</div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title current">Childhood (6-9)</div>
<<if $prologueSelections.childhood>>
<div class="prologue-sidebar-item">
<div>
<div class="prologue-sidebar-item-text">$prologueSelections.childhood.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.childhood.effect</div>
</div>
</div>
<<else>>
<div class="prologue-sidebar-empty">No selection yet</div>
<</if>>
</div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Formative (10-12)</div></div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Adolescent (13-15)</div></div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Coming of Age (16-17)</div></div>
</div>
<div class="prologue-content-area">
<div class="prologue-progress-container">
<div class="prologue-progress-bar">
<div class="prologue-progress-fill" style="width: 33.3%;"></div>
</div>
<div class="prologue-progress-steps">
<span class="prologue-progress-step completed">0-5</span>
<span class="prologue-progress-step active">6-9</span>
<span class="prologue-progress-step">10-12</span>
<span class="prologue-progress-step">13-15</span>
<span class="prologue-progress-step">16-17</span>
</div>
</div>
<div class="prologue-header">
<div class="prologue-age-badge">Ages 6-9</div>
<h1 class="prologue-title">School Days</h1>
<div class="prologue-header-line"></div>
<p class="prologue-subtitle">A whole new world opened up...</p>
</div>
<<image "assets/content/scenes/prologue/childhoodYears.webp" "100%">>
<<narrative>>
Elementary school. Everything changed that day.
<</narrative>>
<<narrative>>
No longer just at home. There was a real world out there now. Classrooms. Rules. New people, so many of them.
<</narrative>>
<<narrative>>
That first day at the school gate, stomach in knots. Mom held your hand tightly. She'd been quiet all morning, more emotional than you expected. When the bell rang, she knelt down, fixed your collar. "You'll be great," she whispered, her eyes a bit watery. "I'm so proud of you."
<</narrative>>
<<narrative>>
Dad had left for work early but came back just for this. He stood behind mom, hands in his pockets, trying to look casual. When you looked back before entering, he gave you a thumbs up and the smile that meant everything would be okay.
<</narrative>>
<<narrative>>
Inside was chaos. Kids crying, parents lingering, teachers trying to organize everyone. But slowly, you adapted. Classmates, recess, waiting in line at the cafeteria, the teacher writing your name on the board... Routines formed.
<</narrative>>
<<narrative>>
Three years later, it was your brother's turn.
<</narrative>>
<<narrative>>
He was terrified. Clung to mom at the gate, wouldn't let go. "I don't want to!" he cried. Mom looked helpless.
<</narrative>>
<<narrative>>
That's when you stepped in. Took his hand. "Come on, I'll show you my old classroom. And I'll be right here at recess, okay?" He looked up at you, tears still there, but nodded.
<</narrative>>
<<narrative>>
Dad watched the whole thing. Later that night at dinner, he brought it up. "You were great with him today," he said. Mom smiled. "She's growing up fast."
<</narrative>>
<<narrative>>
Those years really started shaping you. You can still draw that neighborhood from memory. The kink in the sidewalk where you scraped your knee. Mrs. Keller's dog. The hill.
<</narrative>>
<div class="prologue-memory-section">
<div class="prologue-memory-section-title">Character-Shaping Memories</div>
<div class="prologue-memory-cards" id="childhood-cards">
<div class="prologue-memory-card" data-section="childhood" data-title="Dad's Lessons" data-effect="+15% Willpower" data-stat="willpower" data-value="1.15">
<div class="prologue-memory-title">Dad's Lessons</div>
<div class="prologue-memory-text">
"Some things were hard. Long division. Tying shoelaces in a hurry. Riding that bike without training wheels. Dad would sit with you at the kitchen table every evening. 'One more time,' he'd say, patient in a way he usually wasn't. When you finally got it, his face would light up. 'See? You just needed to stick with it.' Mom watched from the doorway, smiling. He never said he was proud. He didn't have to. You could see it on his face."
</div>
<div class="prologue-memory-effect">+15% Willpower</div>
</div>
<div class="prologue-memory-card" data-section="childhood" data-title="Mom's Encouragement" data-effect="+15% Creativity" data-stat="creativity" data-value="1.15">
<div class="prologue-memory-title">Mom's Encouragement</div>
<div class="prologue-memory-text">
"Art class was your world. You'd come home with drawings and clay projects that barely survived the bus ride. Mom would pin everything on the fridge, no matter how messy. 'You're so creative,' she'd say, examining each one carefully. Dad would joke: 'We're running out of fridge space!' but he never took anything down. When your brother started school, he'd watch you draw for hours. 'Teach me,' he'd beg. And you would. You didn't have mom's patience, exactly. But you'd stop and check, the way she did."
</div>
<div class="prologue-memory-effect">+15% Creativity</div>
</div>
<div class="prologue-memory-card" data-section="childhood" data-title="Family Dinner Talks" data-effect="+15% Intelligence" data-stat="mental" data-value="1.15">
<div class="prologue-memory-title">Family Dinner Talks</div>
<div class="prologue-memory-text">
"Dinner table conversations fascinated you. Every night, dad would talk about his day at work and mom about the neighbors, and you'd ask endless questions. 'Why does that work that way?' 'What happens if...?' Your brother would get bored, playing with his food. You were locked in. 'She's got your brain,' dad would tell mom. 'No, yours,' she'd reply. Some nights you'd still be asking after mom had cleared the plates. Dad would answer one more, then one more, until mom said enough."
</div>
<div class="prologue-memory-effect">+15% Intelligence</div>
</div>
<div class="prologue-memory-card" data-section="childhood" data-title="Mom's Social Butterfly" data-effect="+15% Charisma" data-stat="social" data-value="1.15">
<div class="prologue-memory-title">Mom's Social Butterfly</div>
<div class="prologue-memory-text">
"Making friends was never hard for you. By the second week, you knew everyone's name. Mom would walk you to school and watch you run off to join the group. 'Just like me at that age,' she'd say proudly. When your brother started, you introduced him to everyone. 'This is my brother.' Kids accepted him because of you. Mom saw it all from the gate. 'You're taking care of him,' she'd say later. 'Just like a big sister should.' You didn't think of it as a gift. It just seemed obvious. If someone was alone, you went over."
</div>
<div class="prologue-memory-effect">+15% Charisma</div>
</div>
</div>
</div>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-secondary">Back</span>' "earlyYears">><</link>>
<span id="childhood-continue" class="prologue-btn prologue-btn-primary disabled">Continue</span>
</div>
</div>
</div>
</div>
</div>
<</nobr>>
<<script>>
$(document).ready(function() {
$('#childhood-cards .prologue-memory-card').on('click', function() {
var $card = $(this);
var title = $card.data('title');
var effect = $card.data('effect');
var stat = $card.data('stat');
var value = $card.data('value');
$card.siblings().removeClass('selected');
$card.addClass('selected');
State.variables.prologueSelections.childhood = {
title: title,
effect: effect,
stat: stat,
value: value
};
// Update sidebar - find the current section
var $currentSection = $('.prologue-sidebar-section-title.current').parent();
$currentSection.find('.prologue-sidebar-empty').remove();
$currentSection.find('.prologue-sidebar-item').remove();
$currentSection.append(
'<div class="prologue-sidebar-item"><div>' +
'<div class="prologue-sidebar-item-text">' + title + '</div>' +
'<div class="prologue-sidebar-item-effect">' + effect + '</div>' +
'</div></div>'
);
$('#childhood-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('formativeYears');
});
});
var existing = State.variables.prologueSelections.childhood;
if (existing) {
$('#childhood-cards .prologue-memory-card').each(function() {
if ($(this).data('title') === existing.title) {
$(this).addClass('selected');
$('#childhood-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('formativeYears');
});
}
});
}
});
<</script>>
<<nobr>>
<<set $location = "cityHall">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "City Hall">>
An imposing government building with classical architecture. Citizens come here for permits, licenses, and official business.
<</narrative>>
<<showLocationChars "cityHall">>
<<navMenu>>
<<navCard "downTown">>
<</navMenu>><<nobr>>
<<set $location = "civicCenter">>
<<updateCharacterLocations>>
<<narrative "Civic Center">>
The administrative heart of Old Town. Government buildings, schools, and public services clustered around a central plaza.
<</narrative>>
<<showLocationChars "civicCenter">>
<<navMenu>>
<<navCard "townHall">>
<<navCard "postOffice">>
<<navCard "highSchool">>
<<navCard "hospital">>
<<navCard "policeStation">>
<<navCard "libraryOldtown">>
<<navCard "oldTown">>
<</navMenu>>
<</nobr>><<nobr>><<set $location = "clubPrivate">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Dungeon">>An exclusive members-only club. What happens inside stays inside.<</narrative>>
<<showLocationChars "clubPrivate">>
<<navMenu>><<navCard "redLightCenter">><</navMenu>><<nobr>><<set $location = "clubStrip">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Velvet Rose">>A popular strip club with stages and private rooms. Dollar bills fly.<</narrative>>
<<showLocationChars "clubStrip">>
<<navMenu>><<navCard "basementEntrance">><</navMenu>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<!-- Sidebar -->
<div class="prologue-sidebar">
<div class="prologue-sidebar-title">My Selections</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Early Years (0-5)</div>
<<if $prologueSelections.early>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.early.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.early.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Childhood (6-9)</div>
<<if $prologueSelections.childhood>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.childhood.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.childhood.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Formative (10-12)</div>
<<if $prologueSelections.formative>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.formative.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.formative.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Adolescent (13-15)</div>
<<if $prologueSelections.adolescent>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.adolescent.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.adolescent.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title current">Coming of Age (16-17)</div>
<<if $prologueSelections.coming>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.coming.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.coming.effect</div>
</div></div>
<<else>>
<div class="prologue-sidebar-empty">No selection yet</div>
<</if>>
</div>
</div>
<div class="prologue-content-area">
<div class="prologue-progress-container">
<div class="prologue-progress-bar">
<div class="prologue-progress-fill" style="width: 83.3%;"></div>
</div>
<div class="prologue-progress-steps">
<span class="prologue-progress-step completed">0-5</span>
<span class="prologue-progress-step completed">6-9</span>
<span class="prologue-progress-step completed">10-12</span>
<span class="prologue-progress-step completed">13-15</span>
<span class="prologue-progress-step active">16-17</span>
</div>
</div>
<div class="prologue-header">
<div class="prologue-age-badge">Ages 16-17</div>
<h1 class="prologue-title">On the Edge</h1>
<div class="prologue-header-line"></div>
<p class="prologue-subtitle">Ready for everythingeven if you didn't know what...</p>
</div>
<<image "assets/content/scenes/prologue/comingOfAge.webp" "100%">>
<<narrative>>
Final high school years. Childhood was completely behind you now.
<</narrative>>
<<narrative>>
The reflection in the mirror was familiar. An adult looked back at you. Or at least someone trying to be one.
<</narrative>>
<<narrative>>
At home, everything was changing again. But this time, it wasn't just youit was all of you.
<</narrative>>
<<narrative>>
One evening, dad came home early. Unusual. Mom was already in the kitchen. He looked at both of you. You and your brother at the table doing homework.
<</narrative>>
<<narrative>>
"Family meeting," he said. "Living room. Now."
<</narrative>>
<<narrative>>
Your stomach dropped.
<</narrative>>
<<narrative>>
Mom and dad sat on the couch. You and your brother took the chairs across from them.
<</narrative>>
<<narrative>>
"I got a job offer," dad said. "In the city. It's... a big opportunity."
<</narrative>>
<<narrative>>
Silence.
<</narrative>>
<<narrative>>
"How far?" you asked.
<</narrative>>
<<narrative>>
"Three hours. We'd have to move."
<</narrative>>
<<narrative>>
Your brother's face went pale. "Move? When?"
<</narrative>>
<<narrative>>
"This summer. After you finish the school year."
<</narrative>>
<<narrative>>
Mom reached for dad's hand. "It's a good opportunity," she said softly. "Better pay, better position. And the schools there are excellent."
<</narrative>>
<<narrative>>
You sat there, processing. The small town. Your whole life. All of it, about to be left behind.
<</narrative>>
<<narrative>>
"What if I don't want to go?" you said quietly.
<</narrative>>
<<narrative>>
Dad looked at you. "I know this is hard. But we're a family. We do this together."
<</narrative>>
<<narrative>>
That conversation changed everything. The house that had felt suffocating suddenly felt precious. The town you'd wanted to escape felt like home.
<</narrative>>
<<narrative>>
Your relationship with your parents shifted. They started treating you differently. Not as a child anymore. As someone whose opinion mattered.
<</narrative>>
<<narrative>>
Mom would ask: "What do you think about the new place? Should we get an apartment or a house?" Dad would show you job listings in the city: "What kind of area would you want to live in?"
<</narrative>>
<<narrative>>
Your brother took it harder. He'd lock himself in his room for hours. You'd knock, sit with him, say nothing. Just be there.
<</narrative>>
<<narrative>>
"I don't want to leave my friends," he said one night.
<</narrative>>
<<narrative>>
"I know," you replied. "Me neither."
<</narrative>>
<<narrative>>
"But you're older. You're leaving for college anyway."
<</narrative>>
<<narrative>>
"Doesn't make it easier."
<</narrative>>
<<narrative>>
He looked at you, really looked at you. "You're scared too?"
<</narrative>>
<<narrative>>
"Terrified."
<</narrative>>
<<narrative>>
That honesty brought you closer. You weren't just siblings anymore. You were both scared, and that made you allies.
<</narrative>>
<<narrative>>
School felt different too. Every moment mattered now because it was ending. Friends. Teachers. That one person you'd been crushing on. Time was running out and it made everything sharper.
<</narrative>>
<<narrative>>
One night, at a party, something happened.
<</narrative>>
<<narrative>>
Not your first kiss. That one had been clumsy and forgettable. This was different. Bodies close. Hearts racing. When you got home, face flushed, mom was still up.
<</narrative>>
<<narrative>>
"Good time?" she asked, looking at you carefully.
<</narrative>>
<<narrative>>
"Yeah."
<</narrative>>
<<narrative>>
She smiled, knowing. "Be careful. And... you can talk to me. About anything."
<</narrative>>
<<narrative>>
Her seeing you as a young woman instead of a child. It meant more than any lecture.
<</narrative>>
<<narrative>>
Dad was more awkward about it. He'd clear his throat when boys called the house, hover nearby when you were getting ready for dates. Once, he sat you down.
<</narrative>>
<<narrative>>
"I trust you," he said stiffly. "But... people aren't always... just be smart, okay?"
<</narrative>>
<<narrative>>
You almost laughed at how uncomfortable he looked. But underneath, you felt ithis fear of losing you, of you growing up too fast.
<</narrative>>
<<narrative>>
Your body had changed. Not just how it looked, but how it felt. Awareness. Power. Desire. Things you couldn't talk about with your parents, barely talked about with friends. But they were there, undeniable, part of who you were becoming.
<</narrative>>
<<narrative>>
Those final months at home were a strange mix. Freedom one day, old rules the next.
<</narrative>>
<<narrative>>
You were ready to leave. But also not ready at all.
<</narrative>>
<div class="prologue-memory-section">
<div class="prologue-memory-section-title">Character-Shaping Memories</div>
<div class="prologue-memory-cards" id="coming-cards">
<div class="prologue-memory-card" data-section="coming" data-title="Coming Into Your Own" data-effect="+15% Beauty" data-stat="beauty" data-value="1.15">
<div class="prologue-memory-title">Coming Into Your Own</div>
<div class="prologue-memory-text">
"When you looked in the mirror now, you saw something different. Not a girl anymore. The way you moved, dressed, carried yourself had changed without you deciding. Mom noticed. Sometimes she'd look at you with pride, other times with something like sadness. 'You look just like I did at your age,' she said once, touching your cheek. Dad noticed too, and pretended not to. He'd look away when guys stared at you. Clear his throat when you wore certain outfits. You had power now, and you weren't sure what to do with it yet."
</div>
<div class="prologue-memory-effect">+15% Beauty</div>
</div>
<div class="prologue-memory-card" data-section="coming" data-title="Understanding Desire" data-effect="+15% Charisma" data-stat="charisma" data-value="1.15">
<div class="prologue-memory-title">Understanding Desire</div>
<div class="prologue-memory-text">
"It happened at a party. Or maybe just an evening hanging out. You'd been circling each other for weeks. Glances. Accidental touches. When it finally happened, it wasn't like the movies. It was awkward, then electric. You came home different and you could tell. Mom saw it on your face but didn't push. Dad asked if you had fun, his voice carefully casual. Your brother rolled his eyes. 'You're acting weird.' You weren't weird. You were just paying attention to things you hadn't noticed before."
</div>
<div class="prologue-memory-effect">+15% Charisma</div>
</div>
<div class="prologue-memory-card" data-section="coming" data-title="Breaking Away" data-effect="+15% Practical Skills" data-stat="practical" data-value="1.15">
<div class="prologue-memory-title">Breaking Away</div>
<div class="prologue-memory-text">
"Independence was calling. You got a part-time job, earned your own money, made your own plans. Mom struggled with it. 'Where are you going?' 'When will you be back?' The questions felt less controlling now, more worried. Dad tried to help in his own way. Showed you how to budget, opened a bank account for you, taught you to change a tire. 'You need to know these things,' he said. Your brother watched it all happen, jealous. 'Why does she get to do whatever she wants?' You didn't get to do whatever you wanted. You were learning to negotiate. Turns out that's a skill too."
</div>
<div class="prologue-memory-effect">+15% Practical Skills</div>
</div>
<div class="prologue-memory-card" data-section="coming" data-title="Physical Awakening" data-effect="+15% Physical Skills" data-stat="physical" data-value="1.15">
<div class="prologue-memory-title">Physical Awakening</div>
<div class="prologue-memory-text">
"Dancing, moving, using your body as a means of expression started to mean something different. The way you walked. The way you touched someone's arm in conversation. Mom noticed when you started carrying yourself differently. 'Confidence looks good on you,' she said, but there was concern underneath it. Dad was more obvious. He'd tense up when you hugged him goodbye, like he wasn't sure how to hold you anymore. Your brother said it out loud once. 'Why do you walk like that now?' You hadn't realized you walked differently. But you did."
</div>
<div class="prologue-memory-effect">+15% Physical Skills</div>
</div>
</div>
</div>
<div class="prologue-flashback">
<div class="prologue-flashback-line"></div>
<span class="prologue-flashback-text">Return to Present</span>
<div class="prologue-flashback-line"></div>
</div>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-secondary">Back</span>' "adolescentYears">><</link>>
<span id="coming-continue" class="prologue-btn prologue-btn-primary disabled">Arrive at New Home</span>
</div>
</div>
</div>
</div>
</div>
<</nobr>>
<<script>>
$(document).ready(function() {
$('#coming-cards .prologue-memory-card').on('click', function() {
var $card = $(this);
var title = $card.data('title');
var effect = $card.data('effect');
var stat = $card.data('stat');
var value = $card.data('value');
var special = $card.data('special');
$card.siblings().removeClass('selected');
$card.addClass('selected');
State.variables.prologueSelections.coming = {
title: title,
effect: effect,
stat: stat,
value: value
};
// Special handling for First Kiss - set journal firsts record with random name
if (special === 'firstKiss') {
var boyNames = ['Jake', 'Tyler', 'Ryan', 'Matt', 'Chris', 'Alex', 'Josh', 'Mike', 'Kevin', 'Eric', 'Daniel', 'Nick', 'Sam', 'Ben', 'Luke'];
var randomName = boyNames[Math.floor(Math.random() * boyNames.length)];
State.variables.firsts.firstKiss = randomName;
}
// Update sidebar - find the current section
var $currentSection = $('.prologue-sidebar-section-title.current').parent();
$currentSection.find('.prologue-sidebar-empty').remove();
$currentSection.find('.prologue-sidebar-item').remove();
$currentSection.append(
'<div class="prologue-sidebar-item"><div>' +
'<div class="prologue-sidebar-item-text">' + title + '</div>' +
'<div class="prologue-sidebar-item-effect">' + effect + '</div>' +
'</div></div>'
);
$('#coming-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('newhomeEnter');
});
});
var existing = State.variables.prologueSelections.coming;
if (existing) {
$('#coming-cards .prologue-memory-card').each(function() {
if ($(this).data('title') === existing.title) {
$(this).addClass('selected');
$('#coming-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('newhomeEnter');
});
}
});
}
});
<</script>>
<<nobr>>
<<set $hideTopbarNav = true>>
<<set $hideTopbarTimebox = true>>
<<set $hideTopbarNotifications = true>>
<<set $hideRightbar = true>>
<<set $hideTopbar = true>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="game-setup-container">
<h1 class="page-title">Confirm Your Choices</h1>
<div class="setup-header-line"></div>
<p class="page-subtitle">
Review your character settings before beginning your new life.
</p>
<div class="info-grid">
<!-- 1. IDENTITY (Full Width) -->
<div class="info-card" style="grid-column: 1 / -1;">
<div class="info-card-header">
<span class="icon icon-user"></span> CHARACTER IDENTITY
</div>
<div class="info-card-body">
<div style="text-align: center;">
<div style="font-size: 0.75rem; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.5rem;">You</div>
<div style="font-size: 1.25rem; color: var(--color-accent); font-weight: 600;">$player.firstName $player.lastName</div>
</div>
<div style="text-align: center;">
<div style="display: flex; flex-direction: column; align-items: center; gap: 1rem; padding-top: 1rem;">
<div>
<div style="font-size: 0.75rem; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.5rem;">$characters.mother.status</div>
<div style="font-size: 1.25rem; color: var(--color-accent); font-weight: 600;">$characters.mother.firstName $player.lastName</div>
</div>
<div>
<div style="font-size: 0.75rem; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.5rem;">$characters.father.status</div>
<div style="font-size: 1.25rem; color: var(--color-accent); font-weight: 600;">$characters.father.firstName $player.lastName</div>
</div>
<div>
<div style="font-size: 0.75rem; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.5rem;">$characters.brother.status</div>
<div style="font-size: 1.25rem; color: var(--color-accent); font-weight: 600;margin-bottom: 0.5rem;">$characters.brother.firstName $player.lastName</div>
</div>
</div>
</div>
</div>
</div>
<!-- 2. BODY (Full Width) -->
<div class="info-card" style="grid-column: 1 / -1;">
<div class="info-card-header">
<span class="icon icon-body"></span> BODY DETAILS
</div>
<div class="info-card-body">
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 2rem;">
<div>
<div class="info-line"><span class="label">Eye Color</span> <span class="value">$player.eyeColor</span></div>
<div class="info-line"><span class="label">Hair Color</span> <span class="value">$player.hairColor</span></div>
<div class="info-line"><span class="label">Hair Length</span> <span class="value">$player.hairLength</span></div>
<div class="info-line"><span class="label">Hair Style</span> <span class="value">$player.hairStyle</span></div>
</div>
<div>
<div class="info-line"><span class="label">Height</span> <span class="value">$body.height cm</span></div>
<div class="info-line"><span class="label">Weight</span> <span class="value">$body.weight kg</span></div>
<div class="info-line"><span class="label">BMI</span> <span class="value">$body.bmi</span></div>
<div class="info-line"><span class="label">Body Type</span> <span class="value">$body.bodyType</span></div>
</div>
<div>
<div class="info-line"><span class="label">Bust Size</span> <span class="value">$player.bustSize ($body.bust cm)</span></div>
<div class="info-line"><span class="label">Hip Size</span> <span class="value">$player.hipSize ($body.hips cm)</span></div>
</div>
</div>
</div>
</div>
<!-- 3. SIMULATION (Left Col) -->
<div class="info-card">
<div class="info-card-header">
<span class="icon icon-settings"></span> SIMULATION SETTINGS
</div>
<div class="info-card-body">
<div class="info-line"><span class="label">Hunger Tracking</span> <span class="value"><<if $gameSettings.trackHunger>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Thirst Tracking</span> <span class="value"><<if $gameSettings.trackThirst>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Bladder Tracking</span> <span class="value"><<if $gameSettings.trackBladder>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Calorie Tracking</span> <span class="value"><<if $gameSettings.trackCalories>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Hygiene Requirement</span> <span class="value"><<if $gameSettings.hygieneRequirement>>ON<<else>>OFF<</if>></span></div>
<div class="separator-line"></div>
<div class="info-line"><span class="label">Hair Growth</span> <span class="value"><<if $gameSettings.hairGrowth>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Hair Messiness</span> <span class="value"><<if $gameSettings.hairMessiness>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Body Hair Growth</span> <span class="value"><<if $gameSettings.bodyHairGrowth>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Makeup Fade</span> <span class="value"><<if $gameSettings.makeupWearOff>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Body Weight Changes</span> <span class="value"><<if $gameSettings.bodyDegradation>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Skill Decay</span> <span class="value"><<if $gameSettings.skillDecay>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Relationship Decay</span> <span class="value"><<if $gameSettings.relationshipDecay>>ON<<else>>OFF<</if>></span></div>
</div>
</div>
<!-- 4. CONTENT (Right Col) -->
<div class="info-card">
<div class="info-card-header">
<span class="icon icon-eye"></span> CONTENT PREFERENCES
</div>
<div class="info-card-body">
<div class="info-line"><span class="label">Male Sexual Content</span> <span class="value"><<if $contentPreferences.maleSexual>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Female Sexual Content</span> <span class="value"><<if $contentPreferences.femaleSexual>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Trans / Futa Content</span> <span class="value"><<if $contentPreferences.futaTrans>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Pregnancy Content</span> <span class="value"><<if $contentPreferences.pregnancy>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Incest Content</span> <span class="value"><<if $contentPreferences.incest>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">NTR / Cheating</span> <span class="value"><<if $contentPreferences.ntr>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">BDSM Content</span> <span class="value"><<if $contentPreferences.bdsm>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Non-Consensual</span> <span class="value"><<if $contentPreferences.nonConsensual>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Public Exhibition</span> <span class="value"><<if $contentPreferences.publicExhibition>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Lactation</span> <span class="value"><<if $contentPreferences.lactation>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Foot Fetish</span> <span class="value"><<if $contentPreferences.feet>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Watersports</span> <span class="value"><<if $contentPreferences.watersports>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Scat Content</span> <span class="value"><<if $contentPreferences.scat>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Gore / Violence</span> <span class="value"><<if $contentPreferences.goreViolence>>ON<<else>>OFF<</if>></span></div>
<div class="info-line"><span class="label">Ageplay</span> <span class="value"><<if $contentPreferences.ageplay>>ON<<else>>OFF<</if>></span></div>
</div>
</div>
</div>
<!-- NAVIGATION BUTTONS -->
<div class="form-actions mt-4">
<<btn "Go Back" "settingsPage" "default">><</btn>>
<<btn "Start Life" "prologuePage" "accent">><</btn>>
<<btn "Skip Prologue" "skipPrologue" "accent">><</btn>>
</div>
</div>
</div>
<</nobr>>
<<nobr>><<set $location = "cornerBlock">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Junction Street">>A busy street corner where people gather. Watch your wallet here.<</narrative>>
<<showLocationChars "cornerBlock">>
<<navMenu>><<navCard "southside">><</navMenu>><<nobr>><<set $location = "courtBasketball">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Basketball Courts">>A full-size indoor basketball court. The polished wood floor squeaks under sneakers.<</narrative>>
<<showLocationChars "courtBasketball">>
<<navMenu>><<navCard "recreationCenter">><</navMenu>><<nobr>><<set $location = "courtVolleyball">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Volleyball Courts">>An indoor volleyball court with proper net setup. Perfect for team practice.<</narrative>>
<<showLocationChars "courtVolleyball">>
<<navMenu>><<navCard "recreationCenter">><</navMenu>><<nobr>><<set $location = "dealerCorner">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Drop">>A known spot for illegal transactions. Lookouts watch for police.<</narrative>>
<<showLocationChars "dealerCorner">>
<<navMenu>><<navCard "suburbs">><</navMenu>><<nobr>>
<<set $location = "dinerRubys">>
<<narrative "Back to the Front">>
You step out of the corridor. You reach the counter.
<<= setup.characterDefs.dinerClerk.firstName>> is there – filling a thermos with coffee. He smiles when he sees you.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_entrance_01.webp" "100%">>
<<dialog "dinerClerk">>
How'd it go?
<</dialog>>
<<dialog "player">>
Not bad.
<</dialog>>
<<dialog "dinerClerk">>
Good. A bit loose?
<</dialog>>
<<dialog "player">>
A little. But it's fine.
<</dialog>>
<<dialog "dinerClerk">>
Okay. If it bothers you too much, say so and I'll find a smaller size. First day it's usually like this.
<</dialog>>
<<narrative>>
He sets the thermos on the counter.
<</narrative>>
<<dialog "dinerClerk">>
Now let me introduce you to everyone. You should know the people who work here.
<</dialog>>
<<narrative>>
He turns toward the kitchen.
<</narrative>>
<<dialog "dinerClerk">>
Come with me.
<</dialog>>
<</nobr>>
<div class="btn-center">
<<btn "Continue" "dinerFirstdayJob_kitchen">><</btn>>
</div><<nobr>>
<<set $location = "dinerRubysKitchen">>
<<narrative "To the Dish Station">>
You head back into the kitchen. <<= setup.characterDefs.dinerDishwasher.firstName>> is still at the sink.
<</narrative>>
<<dialog "dinerClerk">>
<<= setup.characterDefs.dinerDishwasher.firstName>>, I'm handing her over to you. You got this.
<</dialog>>
<<dialog "dinerDishwasher">>
Okay <<= setup.characterDefs.dinerClerk.firstName>>. No worries.
<</dialog>>
<<narrative>>
<<= setup.characterDefs.dinerClerk.firstName>> turns to you.
<</narrative>>
<<dialog "dinerClerk">>
Good luck. Say something if you need anything.
<</dialog>>
<<narrative>>
He smiles. Heads out.
<<= setup.characterDefs.dinerDishwasher.firstName>> walks over to you.
<</narrative>>
<<dialog "dinerDishwasher">>
Okay. Come on, I'll show you.
<</dialog>>
<<narrative>>
He goes to the sink. A big two-basin sink – stainless steel. Dishwasher on the left, a wide basin for big pots on the right.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/firstday/dinerDishStation.webp" "100%">>
<<dialog "dinerDishwasher">>
This is your spot. I'm here in the mornings too. Afternoon you're on your own.
<</dialog>>
<<narrative>>
He points to the shelves.
<</narrative>>
<<dialog "dinerDishwasher">>
Dirty plates come here. <<= setup.characterDefs.dinerChef.firstName>> brings them, waitstaff bring them. You take them, wash them.
<</dialog>>
<<narrative>>
He points to the machine.
<</narrative>>
<<dialog "dinerDishwasher">>
Small stuff – plates, cups, silverware – goes in the machine. Rinse with hot water first, then load it. Machine takes 2 minutes.
<</dialog>>
<<narrative>>
He points to the big sink.
<</narrative>>
<<dialog "dinerDishwasher">>
Big stuff – pans, pots – you wash by hand here. Hot water, soap, brush. Scrub hard.
<</dialog>>
<<narrative>>
He points to the gloves.
<</narrative>>
<<dialog "dinerDishwasher">>
Wear these. Water gets real hot. Or you'll burn your hands.
<</dialog>>
<<narrative>>
He points to the sponge, brush, detergent.
<</narrative>>
<<dialog "dinerDishwasher">>
Cleaning supplies. Grab what you need.
<</dialog>>
<<narrative>>
He looks at you.
<</narrative>>
<<dialog "dinerDishwasher">>
Got it?
<</dialog>>
<<dialog "player">>
Got it.
<</dialog>>
<<dialog "dinerDishwasher">>
Good. Let's get started.
<</dialog>>
<<narrative>>
He hands you the gloves.
<</narrative>>
<<dialog "dinerDishwasher">>
Put these on.
<</dialog>>
<<narrative>>
You put them on. Thick rubber gloves. They get hot quick inside.
Right then <<= setup.characterDefs.dinerChef.firstName>> calls out.
<</narrative>>
<<dialog "dinerChef">>
First batch coming!
<</dialog>>
<<narrative>>
He brings a tray full of dirty plates. Sets it on the counter.
<</narrative>>
<<dialog "dinerChef">>
Let's go.
<</dialog>>
<<narrative>>
He heads back to the stove.
<<= setup.characterDefs.dinerDishwasher.firstName>> looks at you.
<</narrative>>
<<dialog "dinerDishwasher">>
Let's get started then.
<</dialog>>
<</nobr>>
<div class="btn-center">
<<btn "Start work" "">>
<<silently>>
<<jobExecuteShift 120>>
<</silently>>
<<if $jobShiftSuccess>>
<<set $jobState.firstWorkDayEventShown = true>>
<<goto "dinerRubys">>
<<else>>
<<goto "dinerFirstdayJob_dishwasher">>
<</if>>
<</btn>>
</div><<nobr>>
<<set $location = "dinerRubysDressingRoom">>
<<narrative "Dressing Room">>
You walk down a narrow corridor. On the left, the kitchen entrance – the sound of burners, sizzling pans from inside. Steam pours out.
On the right, two doors. One has a small sign that says "STORAGE." The other is plain, painted metal.
<</narrative>>
<<image "assets/content/locations/oldTown/dinerRubys/dinerCorridor.webp" "100%">>
<<dialog "dinerClerk">>
This is the dressing room. Everyone gets ready here.
<</dialog>>
<<narrative>>
<<= setup.characterDefs.dinerClerk.firstName>> opens the second door.
<</narrative>>
<<image "assets/content/locations/oldTown/dinerRubys/dinerDressingRoom.webp" "100%">>
<<narrative>>
A small, cramped room. Metal lockers on the walls – most locked, some open. An old wooden bench in the middle, paint flaking off. A fluorescent light flickers on the ceiling. A "NO SMOKING" sign, next to a "WASH YOUR HANDS" poster – yellowed, torn.
A trash can in one corner, hangers on the other.
<<= setup.characterDefs.dinerClerk.firstName>> goes to one of the lockers and opens it. Inside are uniforms – different sizes, stacked.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/firstday/dinerTomUniform.webp" "100%">>
<<dialog "dinerClerk">>
What size do you wear?
<</dialog>>
<<dialog "player">>
Small.
<</dialog>>
<<dialog "dinerClerk">>
Okay. One minute.
<</dialog>>
<<silently>>
<<set _uniformIds = ["topRubysDishwash", "bottomRubysDishwash", "apronRubysDishwash"]>>
<<set $wardrobe.owned = $wardrobe.owned || []>>
<<for _id range _uniformIds>>
<<if !$wardrobe.owned.includes(_id)>><<set $wardrobe.owned = $wardrobe.owned.concat([_id])>><</if>>
<</for>>
<</silently>>
<<narrative>>
He rummages inside. Pulls out a top, grabs the pants, adds the apron.
<</narrative>>
<<dialog "dinerClerk">>
Here. Top, pants, apron. All here.
<</dialog>>
<<narrative>>
He holds them out. The uniforms are heavy, thick fabric. They look clean.
<</narrative>>
<<dialog "dinerClerk">>
Try them on. If they don't fit, say so and I'll get another size. If they're too big, don't just deal with it – swap them.
<</dialog>>
<<narrative>>
He turns toward the door.
<</narrative>>
<<dialog "dinerClerk">>
I'll be outside. Come out when you're ready.
<</dialog>>
<<dialog "player">>
Alright.
<</dialog>>
<<narrative>>
He opens the door, steps into the corridor. Closes it behind him.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Continue" "dinerFirstdayJob_gettingDressed">><</btn>>
</div><<nobr>>
<<set $location = "dinerRubysCorridor">>
<<narrative "Encounter">>
You open the door. You're in the corridor.
A bit further down – on the left – another door opens. The one that says "STORAGE."
A blonde girl steps out. She's holding an empty tray. She's wearing a pink shirt – waitress uniform. Her hair is messy, she looks tired.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/firstday/dinerEmmaEncounter.webp" "100%">>
<<narrative>>
Your eyes meet.
She pauses for a moment. She sizes you up – new uniform, new face.
Then a slight smile. A nod. Polite.
She doesn't say anything. She walks down the corridor, passes you. Heads toward the other end – the front, the counter.
You stay where you are. You turn your head, watch out of the corner of your eye.
She walks away. You watch until she's gone.
<</narrative>>
<<image "assets/content/locations/oldTown/dinerRubys/dinerStorage.webp" "100%">>
<<narrative>>
Then you look at the storage room door. Still half open. The faint smell of coffee from inside. A sound – maybe a radio. A chair creaking.
Must be some kind of break room.
The door slowly closes.
You turn forward. You walk down the corridor toward the front.
<</narrative>>
<<silently>>
<<set $characters.dinerWaitress1.known = true>>
<<if !$characters.dinerWaitress1.firstMet>><<set $characters.dinerWaitress1.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>><</if>>
<</silently>>
<</nobr>>
<div class="btn-center">
<<btn "Continue" "dinerFirstdayJob_backToFront">><</btn>>
</div>/* ========================================
JOB - First work day event (one-time)
Ruby's Diner. Clerk (Tom) greets you.
======================================== */
<<nobr>>
<<set $location = "dinerRubys">>
<<narrative "First Work Day – Ruby's Diner">>
You step through the door of Ruby's Diner. Morning. The smell of coffee, fried bacon, a faint wet-floor scent. Steam rises from the kitchen, dishes clatter.
<<= setup.characterDefs.dinerClerk.firstName>> stands behind the counter. He's busy with the register – counting change, checking receipts. He looks up when he sees you.
<<image "assets/content/scenes/oldtown/rubysDiner/firstday/dinerTomGreeting.webp" "100%">>
<</narrative>>
<<dialog "dinerClerk">>
Oh, you made it.
<</dialog>>
<<narrative>>
He smiles. Calm, friendly.
<</narrative>>
<<dialog "player">>
Good morning <<= setup.characterDefs.dinerClerk.firstName>>.
<</dialog>>
<<dialog "dinerClerk">>
Good morning. Ready?
<</dialog>>
<<dialog "player">>
I think so.
<</dialog>>
<<dialog "dinerClerk">>
Don't worry. Nothing hard. I'll show you what to do.
<</dialog>>
<<narrative>>
He closes the register and walks toward you.
<</narrative>>
<<dialog "dinerClerk">>
Come on, let's get you into uniform first.
<</dialog>>
<<narrative>>
He passes the counter and leads you toward the back.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Continue" "dinerFirstdayJob_dressingRoom">><</btn>>
</div><<nobr>>
<<set $location = "dinerRubys">>
<<narrative "To the Front">>
You leave the kitchen. You reach the front.
Three people by the counter.
One – the blonde girl from the corridor. You see her up close now. Young, twenties. Tired but friendly. She's holding an order pad.
Another – a young dark-haired girl. Wearing a navy blue shirt. Calm, serious. Wiping glasses.
The third – a young man in a white t-shirt. Dark hair, good-looking. Organizing menus.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/firstday/dinerWaitstaffGroup.webp" "100%">>
<<dialog "dinerClerk">>
<<= setup.characterDefs.dinerWaitress1.firstName>>, <<= setup.characterDefs.dinerWaitress2.firstName>>, <<= setup.characterDefs.dinerWaitress3.firstName>>.
<</dialog>>
<<narrative>>
The blonde – <<= setup.characterDefs.dinerWaitress1.firstName>> – looks up. She gives you a slight smile when she sees you. She recognizes you – the corridor.
<</narrative>>
<<dialog "dinerWaitress1">>
Hi. I'm <<= setup.characterDefs.dinerWaitress1.firstName>>.
<</dialog>>
<<narrative>>
Her voice is soft, warm.
The dark-haired girl – <<= setup.characterDefs.dinerWaitress2.firstName>> – looks up.
<</narrative>>
<<dialog "dinerWaitress2">>
<<= setup.characterDefs.dinerWaitress2.firstName>>.
<</dialog>>
<<narrative>>
Short. She doesn't smile but she's not hostile either. Just distant.
The man in the white t-shirt – <<= setup.characterDefs.dinerWaitress3.firstName>> – approaches. Holds out his hand.
<</narrative>>
<<dialog "dinerWaitress3">>
<<= setup.characterDefs.dinerWaitress3.firstName>>. Welcome.
<</dialog>>
<<narrative>>
You shake hands.
<</narrative>>
<<dialog "dinerWaitress3">>
You here for dishwashing?
<</dialog>>
<<dialog "player">>
Yeah.
<</dialog>>
<<dialog "dinerWaitress3">>
Good. <<= setup.characterDefs.dinerDishwasher.firstName>> is a good guy. He'll help you out.
<</dialog>>
<<narrative>>
The manager's office door opens. <<= setup.characterDefs.dinerManager.firstName>> steps out – the same man from your interview. He sees you in the uniform, gives a short nod.
<</narrative>>
<<dialog "dinerManager">>
Good. Welcome to the team. I'll leave you to it.
<</dialog>>
<<narrative>>
He heads back into his office.
<</narrative>>
<<dialog "dinerClerk">>
Okay. Everyone's met. Let's get to work.
<</dialog>>
<<narrative>>
He turns to you.
<</narrative>>
<<dialog "dinerClerk">>
Come on, I'll take you there.
<</dialog>>
<<silently>>
<<set $characters.dinerManager.known = true>>
<<if !$characters.dinerManager.firstMet>><<set $characters.dinerManager.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>><</if>>
<<set $characters.dinerWaitress2.known = true>>
<<set $characters.dinerWaitress3.known = true>>
<<if !$characters.dinerWaitress2.firstMet>><<set $characters.dinerWaitress2.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>><</if>>
<<if !$characters.dinerWaitress3.firstMet>><<set $characters.dinerWaitress3.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>><</if>>
<</silently>>
<</nobr>>
<div class="btn-center">
<<btn "Continue" "dinerFirstdayJob_dishwasher">><</btn>>
</div><<nobr>>
<<set $location = "dinerRubysDressingRoom">>
<<narrative "Getting Dressed">>
You're alone.
You look around. Walk toward the lockers.
Some lockers have names on them – labels, written in faded pen.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/firstday/dinerLockersNames.webp" "100%">>
<<narrative>>
One says "EMMA." Next to it "SOFIA." Another "JAKE."
One locker door is ajar – inside, a pink sweater hangs, lip gloss, a perfume bottle.
Another locker has "JAMES" on it. Locked.
Is everyone getting changed here? Men and women – all in the same room?
Weird. But I guess those are the rules.
You set the uniforms on the bench. Unfold the pants first – dark gray, sturdy fabric. The top matches. The apron is thick, water-resistant – black, long.
You start undressing. Put your own clothes on the bench. Pull on the pants – the waist is a bit loose but they'll do. The top – sleeves fit. You take the apron and tie it behind your neck.
You look at the lockers again. The names. So everyone gets ready here. Same place. They come in the morning, get changed, head out.
There's a small, cracked mirror on the wall. You look at yourself. Strange. First time wearing a uniform like this. Not bad though.
You're ready.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Put on uniform" "dinerFirstdayJob_wardrobe">><</btn>>
</div><<nobr>>
<<set $location = "dinerRubysKitchen">>
<<narrative "Entering the Kitchen">>
You step into the kitchen. A narrow, hot space. Burners blazing, pans sizzling. Steam everywhere.
Two people inside.
One – an older man with a beard – at the stove. Cracking eggs, making an omelet. Calm, slow movements. He looks experienced.
The other – a young man with scars – at the sink. Washing dishes. Sleeves rolled up, apron wet.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/firstday/dinerKitchenWorking.webp" "100%">>
<<dialog "dinerClerk">>
<<= setup.characterDefs.dinerChef.firstName>>, <<= setup.characterDefs.dinerDishwasher.firstName>>.
<</dialog>>
<<narrative>>
They both look up.
<</narrative>>
<<dialog "dinerClerk">>
Our new teammate. Starting today. Dishwashing.
<</dialog>>
<<narrative>>
The bearded man – <<= setup.characterDefs.dinerChef.firstName>> – puts down the spatula. He looks at you. His eyes are sharp but not hostile. Just sizing you up.
<</narrative>>
<<dialog "dinerChef">>
What's your name?
<</dialog>>
<<dialog "player">>
<<= $player.firstName>>.
<</dialog>>
<<dialog "dinerChef">>
I'm <<= setup.characterDefs.dinerChef.firstName>>. I run the kitchen here.
<</dialog>>
<<narrative>>
He nods. Short, distant.
<</narrative>>
<<dialog "dinerChef">>
Speed matters here. If dishes pile up, we stop. Got it?
<</dialog>>
<<dialog "player">>
Got it.
<</dialog>>
<<dialog "dinerChef">>
Good.
<</dialog>>
<<narrative>>
He turns back to flip the omelet. Conversation over as far as he's concerned.
The scarred man – <<= setup.characterDefs.dinerDishwasher.firstName>> – wipes his hands on a towel. He walks over. Smiles.
<</narrative>>
<<dialog "dinerDishwasher">>
Hey, I'm <<= setup.characterDefs.dinerDishwasher.firstName>>. I'll be working with you.
<</dialog>>
<<narrative>>
He holds out his hand. You shake. His hand is wet but warm.
<</narrative>>
<<dialog "dinerDishwasher">>
First day?
<</dialog>>
<<dialog "player">>
Yeah.
<</dialog>>
<<dialog "dinerDishwasher">>
I get it. I was nervous my first day too. But you'll get used to it. Easy job, really. Just gotta be fast.
<</dialog>>
<<narrative>>
He talks with a slight accent – maybe Spanish. Friendly, relaxed.
<</narrative>>
<<dialog "dinerDishwasher">>
I'll show you everything in a bit. Don't worry.
<</dialog>>
<<dialog "dinerClerk">>
Alright. Let's keep going.
<</dialog>>
<<silently>>
<<set $characters.dinerChef.known = true>>
<<set $characters.dinerDishwasher.known = true>>
<<if !$characters.dinerChef.firstMet>><<set $characters.dinerChef.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>><</if>>
<<if !$characters.dinerDishwasher.firstMet>><<set $characters.dinerDishwasher.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>><</if>>
<</silently>>
<</nobr>>
<div class="btn-center">
<<btn "Continue" "dinerFirstdayJob_front">><</btn>>
</div><<set $location = "dinerRubysDressingRoom">>
/* Wear work uniform (top, bottom, apron) to leave; no back button. */
<<wardrobe "rubysDiner" "dinerFirstdayJob_emmaEncounter" "noBack" "ruby_dishwasher">><<nobr>>
<<set $location = "dinerRubys">>
<<updateCharacterLocations>>
<<narrative "Ruby's Diner">>
A classic American diner with red vinyl booths. Burgers, milkshakes, and good company.
<</narrative>>
<<showLocationChars "dinerRubys">>
<<questPrompts>>
<div class="location-actions">
<<restaurantLink "dinerRubys">>
<<jobWorkButton "dinerRubys">>
</div>
<<if $job && $job.id && setup.jobs[$job.id] && setup.jobs[$job.id].workplace === "dinerRubys">>
<<navMenu>>
<<navCard "dinerRubysKitchen">>
<<navCard "dinerRubysStorage">>
<<navCard "dinerRubysDressingRoom">>
<<navCard "dinerRubysManagerOffice">>
<<navCard "dinerRubysBathroom">>
<<navCard "oldTown">>
<</navMenu>>
<<else>>
<<navMenu>>
<<navCard "dinerRubysBathroom">>
<<navCard "oldTown">>
<</navMenu>>
<</if>>
<</nobr>><<nobr>>
<<set $location = "dinerRubysBathroom">>
<<set $returnAfterToilet = "dinerRubysBathroom">>
<<updateCharacterLocations>>
<<narrative "Bathroom">>
A small restroom off the main dining area. Clean and functional.
<</narrative>>
<div class="location-actions">
<<bathroomActions>>
<<mirrorBtn>>
</div>
<<navMenu>>
<<navCard "dinerRubysKitchen">>
<<navCard "dinerRubysStorage">>
<<navCard "dinerRubysDressingRoom">>
<<navCard "dinerRubysManagerOffice">>
<<navCard "dinerRubys">>
<<navCard "oldTown">>
<</navMenu>>
<</nobr>><div class="divnobr">
<<set $location = "dinerRubysDressingRoom">>
<<updateCharacterLocations>>
<<narrative "Employee Dressing Room">>
A small room with lockers and a bench. Staff change here before shifts.
<</narrative>>
</div>
<<wardrobe>><<nobr>>
<<set $location = "dinerRubysKitchen">>
<<updateCharacterLocations>>
<<narrative "Kitchen">>
The back kitchen. Stainless steel counters, the grill, the dishwasher station. The heart of the diner.
<</narrative>>
<<showLocationChars "dinerRubysKitchen">>
<<navMenu>>
<<navCard "dinerRubysStorage">>
<<navCard "dinerRubysDressingRoom">>
<<navCard "dinerRubysManagerOffice">>
<<navCard "dinerRubysBathroom">>
<<navCard "dinerRubys">>
<<navCard "oldTown">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "dinerRubysManagerOffice">>
<<updateCharacterLocations>>
<<narrative "Manager's Office">>
Vince's office. Desk, paperwork, a view of the diner through the window.
<</narrative>>
<<showLocationChars "dinerRubysManagerOffice">>
<<navMenu>>
<<navCard "dinerRubysKitchen">>
<<navCard "dinerRubysStorage">>
<<navCard "dinerRubysDressingRoom">>
<<navCard "dinerRubysBathroom">>
<<navCard "dinerRubys">>
<<navCard "oldTown">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "dinerRubysStorage">>
<<updateCharacterLocations>>
<<narrative "Storage">>
Shelves of canned goods, dry supplies, and cleaning products. The diner's backstage. A small break corner by the back wallstool, coffee machine, and a place to sit for a few minutes.
<</narrative>>
<<narrative>>
You can grab something to eat once per day, coffee anytime, and take a short rest here.
<</narrative>>
<div class="location-actions" style="margin-bottom: 0.5em;">
<<if !$jobState.freeMealUsedToday>>
<<btn "Eat food" "dinerRubysStorage_freeMeal">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Already had your meal today"><span class="icon icon-lock icon-12"></span> Eat food</span>
<</if>>
<<btn "Coffee" "dinerRubysStorage_coffee">><</btn>>
<<btn "Rest" "dinerRubysStorage_rest">><</btn>>
</div>
<<showLocationChars "dinerRubysStorage">>
<<navMenu>>
<<navCard "dinerRubysKitchen">>
<<navCard "dinerRubysDressingRoom">>
<<navCard "dinerRubysManagerOffice">>
<<navCard "dinerRubysBathroom">>
<<navCard "dinerRubys">>
<<navCard "oldTown">>
<</navMenu>>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "dinerRubysStorage">>
<<if !$job || !$job.id || !setup.jobs[$job.id] || setup.jobs[$job.id].workplace !== "dinerRubys">>
<<goto "dinerRubysStorage">>
<</if>>
<<advanceTime 15>>
<<loseStat "thirst" 30>>
<<gainStat "energy" 25>>
<<flushNotifications>>
<<run window.notifyStaffCoffeeToast()>>
<<recalculateStats>>
<<set _sceneIndex = random(1, 3)>>
<</silently>>
<<narrative "Coffee">>
You pour yourself a cup from the staff machine. Hot, strong, and on the house. You take a few minutes to drink it.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/oldtown/rubysDiner/storage/coffee/coffee1.mp4" "100%">>
<<elseif _sceneIndex === 2>>
<<vid "assets/content/scenes/oldtown/rubysDiner/storage/coffee/coffee2.mp4" "100%">>
<<else>>
<<vid "assets/content/scenes/oldtown/rubysDiner/storage/coffee/coffee3.mp4" "100%">>
<</if>>
<div class="btn-center">
<<btn "Back" "dinerRubysStorage">><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "dinerRubysStorage">>
<<if !$job || !$job.id || !setup.jobs[$job.id] || setup.jobs[$job.id].workplace !== "dinerRubys">>
<<goto "dinerRubysStorage">>
<</if>>
<<if $jobState.freeMealUsedToday>>
<<goto "dinerRubysStorage">>
<</if>>
<<set $jobState.freeMealUsedToday = true>>
<<eatFood 750 100>>
<<loseStat "thirst" 100>>
<<gainStat "energy" 45>>
<<gainStat "mood" 10>>
<<gainStat "health" 2>>
<<loseStat "stress" 7>>
<<advanceTime 30 "relax">>
<<flushNotifications>>
<<run window.notifyStaffMealToast()>>
<<set _sceneIndex = random(1, 2)>>
<<recalculateStats>>
<</silently>>
<<narrative "Staff meal">>
You grab a quick staff meal from the backtoday it's a burger and fries. You eat by the storage shelves, refueling before heading back.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/oldtown/rubysDiner/storage/eatfood/eatFood1.mp4" "100%">>
<<else>>
<<vid "assets/content/scenes/oldtown/rubysDiner/storage/eatfood/eatFood2.mp4" "100%">>
<</if>>
<div class="btn-center">
<<btn "Back" "dinerRubysStorage">><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "dinerRubysStorage">>
<<if !$job || !$job.id || !setup.jobs[$job.id] || setup.jobs[$job.id].workplace !== "dinerRubys">>
<<goto "dinerRubysStorage">>
<</if>>
<<advanceTime 30 "relax">>
<<loseStat "stress" 25>>
<<gainStat "energy" 25>>
<<gainStat "mood" 10>>
<<flushNotifications>>
<<run window.notifySuccess("Short rest – energy and mood restored.")>>
<<recalculateStats>>
<</silently>>
<<narrative "Short rest">>
You sit in the break corner for half an hour. No one bothers you. You breathe, stretch your shoulders, and feel more ready to go back.
<</narrative>>
<div class="btn-center">
<<btn "Back" "dinerRubysStorage">><</btn>>
</div>
<</nobr>><<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _v = random(1, 3)>>
<<set _picNum = random(1, 4)>>
<<if _v === 1>>
<<set _brokenImg = "assets/content/scenes/oldtown/rubysDiner/work/dishwash/pics/plateBroken" + _picNum + ".webp">>
<<elseif _v === 2>>
<<set _brokenImg = "assets/content/scenes/oldtown/rubysDiner/work/dishwash/pics/brokenGlass" + _picNum + ".webp">>
<<else>>
<<set _brokenImg = "assets/content/scenes/oldtown/rubysDiner/work/dishwash/pics/mugBroken" + _picNum + ".webp">>
<</if>>
<</silently>>
<<if _v === 1>>
<<narrative "Accident">>
<<image _brokenImg "100%">>
The plate slips out of the rack before you can catch it. It hits the tiles and splits clean in two. The sound cuts through the kitchen noise.
James looks up from the grill.
<</narrative>>
<<dialog "dinerChef">>
Sweep it up. Don't leave it.
<</dialog>>
<<narrative>>
He goes back to what he was doing. No lecture. Just that.
You grab the broom.
<</narrative>>
<<elseif _v === 2>>
<<narrative "Accident">>
<<image _brokenImg "100%">>
A glass tips while you're rinsing it right off the edge of the basin. It shatters on the floor next to your foot.
Mike glances over from the machine.
<</narrative>>
<<dialog "dinerDishwasher">>
Oof. Don't let Vince see that.
<</dialog>>
<<narrative>>
He tosses you a paper towel and goes back to loading.
<</narrative>>
<<else>>
<<narrative "Accident">>
<<image _brokenImg "100%">>
The mug handle snaps off mid-rinse. The body of it cracks against the sink. Small pieces, but loud enough.
Vince is in the kitchen doorway a second later. You didn't hear him come in.
<</narrative>>
<<dialog "dinerManager">>
Everything alright back here?
<</dialog>>
<<narrative>>
He looks at the mug in your hand. Then at you. He leaves without waiting for an answer.
<</narrative>>
<</if>>
<<silently>>
<<loseStat "mood" 3>>
<<set $jobState.weeklyDeductions = ($jobState.weeklyDeductions || 0) + 3>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>><<silently>>
<<set _v = random(1, 3)>>
<<set $location = $activityOrigin || "dinerRubys">>
<</silently>>
<<if _v === 1>>
<<narrative "Accident">>
You grab a pot straight off the pass without thinking. The handle is still hot from the stove.
You let go fast. Not fast enough. A red line across your palm.
<</narrative>>
<<narrative>>
You run cold water over it and keep going.
<</narrative>>
<<elseif _v === 2>>
<<narrative "Accident">>
Steam hits your forearm when you open the washer too early. A sharp sting that fades into a dull pink mark.
You pull your sleeve down and keep working.
<</narrative>>
<<else>>
<<narrative "Accident">>
A splash of near-boiling water catches the back of your hand while you're draining a pot. You flinch and almost drop it.
Sofia is passing through with a tray.
<</narrative>>
<<dialog "dinerWaitress3">>
Hey you okay? Hold on.
<</dialog>>
<<narrative>>
She comes back a minute later with a wet napkin and a small bandage from the first-aid box. Doesn't say much else. Goes back to her tables.
<</narrative>>
<<silently>>
<<gainCharacterStat "dinerWaitress3" "friendship" 1>>
<</silently>>
<</if>>
<<silently>>
<<loseStat "health" 2>>
<<loseStat "mood" 2>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>><<silently>>
<<set _v = random(1, 3)>>
<<set $location = $activityOrigin || "dinerRubys">>
<</silently>>
<<if _v === 1>>
<<narrative "Problem">>
The water stops draining. You keep rinsing for a second before you notice it's rising. Greasy, grey water climbing up the basin.
You pull out the filter. A thick lump of food scraps and napkin bits. You clear it with your fingers.
<</narrative>>
<<narrative>>
The water goes down. Your hands smell like the bottom of the drain.
<</narrative>>
<<elseif _v === 2>>
<<narrative "Problem">>
Mid-stack, the sink starts backing up. Brownish water pushing up through the drain. You stop the tap and wait. It doesn't go down.
You reach in and pull out whatever's blocking it. Something soft and wet comes loose.
<</narrative>>
<<narrative>>
It drains. You rinse your hands longer than usual.
<</narrative>>
<<else>>
<<narrative "Problem">>
The left basin floods. Water spills over the edge and onto the floor before you can shut it off.
Mike looks over.
<</narrative>>
<<dialog "dinerDishwasher">>
Drain's clogged again. Happens every couple weeks.
<</dialog>>
<<narrative>>
You clear the blockage and mop the floor. Ten minutes you won't get back.
<</narrative>>
<</if>>
<<silently>>
<<loseStat "energy" 5>>
<<gainStat "stress" 3>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>/* ==========================================
EVENT - Diana Arrival
Triggers after vince_day3_family + 2 days, full dish shift; player taps through from shift outro.
end of shift. Player witnesses from hallway.
Voice: <<narrative>> to player (*you* ok); inner = dialog (Inner voice), *I*.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<set $flags.dianaEventShown = true>>
<<set $flags.dianaGossipUnlocked = false>>
<<advanceTime 15>>
<</silently>>
<<narrative "Corridor">>
You're on your way out end of the hallway, one step from the dining floor.
<</narrative>>
<<dialog "dinerWaitress1">>
(passing by, plates in hand, half to herself)
...her again.
<</dialog>>
<<narrative>>
Emma doesn't stop. Doesn't look at you.
You reach the end of the corridor. Before you step onto the floor, the front door opens.
You stop.
<</narrative>>
<<narrative "Diner">>
Red dress. Shoulders bare. The perfume reaches you before she does.
<</narrative>>
<<image "assets/content/scenes/events/diana/diana_arrival.webp" "50%">>
<<narrative>>
Tom looks up from behind the counter, by the register:
<</narrative>>
<<dialog "dinerClerk">>
Diana! Welcome
<</dialog>>
<<dialog "dianaRuby">>
Good to be here.
<</dialog>>
<<dialog "dinerClerk">>
Let me get Vince for you
<</dialog>>
<<narrative>>
He reaches for the phone. Just as his hand touches it, the kitchen door swings open.
Vince.
His eyes land on Diana immediately. He smiles.
<</narrative>>
<<dialog "dinerManager">>
Hey, babe. Did I keep you waiting?
<</dialog>>
<<dialog "dianaRuby">>
Not at all.
<</dialog>>
<<narrative>>
Vince crosses the floor toward her. He presses a kiss to her mouth and his hand slides down. Settled at her hip, then lower. She adjusts her bag on her shoulder. Doesn't flinch.
<</narrative>>
<<image "assets/content/scenes/events/diana/diana_vince_touch.gif" "100%">>
<<dialog "dianaRuby">>
Where are we going?
<</dialog>>
<<dialog "dinerManager">>
Surprise.
<</dialog>>
<<narrative>>
Somewhere to your left, Emma is wiping down a booth. Fast, tight movements. She isn't looking at them but she's listening.
<</narrative>>
<<narrative "Corridor">>
You're still at the end of the corridor. Watching.
<</narrative>>
<<dialog "player">>
(Inner voice)
I'm right here and he still doesn't care who sees.
Like this is the most normal thing in the world.
<</dialog>>
<<dialog "dinerManager">>
Text me if anything comes up, Tom. I'm not coming in for closing.
<</dialog>>
<<dialog "dinerClerk">>
Sure thing, boss.
<</dialog>>
<<narrative>>
Diana walks toward the door. As she passes, Emma looks up for the first time. Their eyes meet.
Diana smiles. Polite. Shallow.
Emma doesn't smile back.
The door closes.
Emma wipes her hands on her apron and watches them go. Then she turns around and goes back to work.
<</narrative>>
<div class="location-actions">
<<btn "Head back to the kitchen" "dinerWork_event_dianaKitchen">><</btn>>
</div>
</div>/* ==========================================
EVENT - Diana Kitchen
Follows dianaArrival. Player eavesdrops
from the kitchen door.
Voice: <<narrative>> to player (*you* ok); inner = dialog (Inner voice), *I*.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<advanceTime 20>>
<</silently>>
<<narrative "Kitchen Door">>
You head toward the kitchen. Through the door, voices.
<</narrative>>
<<image "assets/content/locations/oldTown/dinerRubys/dinerKitchen.webp" "100%">>
<<narrative>>
You stop.
<</narrative>>
<<dialog "dinerWaitress3">>
Who is that? Is she new?
<</dialog>>
<<dialog "dinerChef">>
Same woman. She's been coming for months. Probably one of Vince's regulars who knows how many he's got.
<</dialog>>
<<dialog "dinerDishwasher">>
Man, he just grabbed her ass right there in the middle of the floor. No shame at all.
<</dialog>>
<<dialog "dinerWaitress3">>
You see that ass though? Damn. You could never get enough of that.
<</dialog>>
<<dialog "dinerChef">>
She didn't even flinch. She's used to it. What's she gonna do if that's every day.
<</dialog>>
<<dialog "dinerDishwasher">>
They fuck good too, I guarantee it. Woman like that knows what she wants in bed.
<</dialog>>
<<dialog "dinerWaitress3">>
Holy shit. If I were Vince I'd never come in to work.
<</dialog>>
<<dialog "dinerChef">>
I'd go all night. Wake up and go again.
<</dialog>>
<<dialog "dinerDishwasher">>
(laughing)
Yeah right, you couldn't handle it.
<</dialog>>
<<narrative>>
They laughed.
<</narrative>>
<<narrative>>
You swallow.
<</narrative>>
<<narrative>>
The words keep coming, easy in their mouths.
<</narrative>>
<<narrative>>
She got to all of them that much. Why? What did she have?
<</narrative>>
<<narrative>>
The red dress? The perfume? That walk that way she didn't seem to care about any of it?
<</narrative>>
<<dialog "player">>
(Inner voice)
I shouldn't be listening to this.
But I can't walk away. I can't make myself go in either.
What the hell am I even doing here?
<</dialog>>
<<narrative>>
You do neither.
<</narrative>>
<<narrative>>
Hard steps in the corridor. Emma two empty plates in her hands, face like stone.
<</narrative>>
<<narrative>>
She reaches the door and sees you. Stops.
<</narrative>>
<<dialog "dinerWaitress1">>
What are you doing standing here?
<</dialog>>
<<narrative "Kitchen">>
She doesn't wait for an answer. She pushes through the door.
<</narrative>>
<<narrative>>
You follow her in.
<</narrative>>
<<narrative>>
The men go quiet the second they hear her voice. Someone laughs. Then the others. Everyone finds something to do suddenly very busy.
<</narrative>>
<<narrative>>
Emma sets the plates on the counter. Back to you. Doesn't say a word.
<</narrative>>
<<narrative>>
You turn to Mike.
<</narrative>>
<<dialog "player">>
My shift's done. I'm heading out.
<</dialog>>
<<dialog "dinerDishwasher">>
(without looking up)
Okay.
<</dialog>>
<<narrative>>
You take off your apron. Hang it up.
<</narrative>>
<<narrative>>
You walk out.
<</narrative>>
<div class="location-actions">
<<btn "Leave the diner" "dinerRubys">><</btn>>
</div>
</div><<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _v = random(1, 3)>>
<</silently>>
<<if _v === 1>>
<<narrative>>
Emma comes in to drop off a tray. On her way back out she sets a glass of water on the counter next to you without looking up.
<</narrative>>
<<dialog "dinerWaitress1">>
Drink something.
<</dialog>>
<<narrative>>
She's already gone.
<</narrative>>
<<elseif _v === 2>>
<<narrative>>
Emma passes through between tables. She stops beside you, fills a glass from the tap, and puts it down by the sink.
<</narrative>>
<<dialog "dinerWaitress1">>
You look like you need it.
<</dialog>>
<<narrative>>
She doesn't wait for a response.
<</narrative>>
<<else>>
<<narrative>>
A glass appears on the corner of your station. You didn't see who left it. Emma is already back on the floor, pen out, notepad up.
Cold water. You drink it.
<</narrative>>
<</if>>
<<silently>>
<<if $gameSettings.trackThirst>>
<<loseStat "thirst" 25>>
<</if>>
<<gainCharacterStat "dinerWaitress1" "friendship" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>><<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _v = random(1, 3)>>
<</silently>>
<<if _v === 1>>
<<narrative>>
James slides a small bowl of fries onto the edge of your station. Doesn't stop moving.
<</narrative>>
<<dialog "dinerChef">>
Eat.
<</dialog>>
<<narrative>>
That's it. He's back at the grill before you can say anything.
<</narrative>>
<<elseif _v === 2>>
<<narrative>>
A napkin lands next to the sink. You look over. James has already turned back to the stove. There's a piece of grilled chicken on it small, trimmed off something larger.
You eat it standing up.
<</narrative>>
<<else>>
<<narrative>>
James comes by with a small plate of bread ends leftover from kitchen prep, no use to anyone now.
<</narrative>>
<<dialog "dinerChef">>
It's just scraps. Don't make it weird.
<</dialog>>
<<narrative>>
He sets it down and walks off.
<</narrative>>
<</if>>
<<silently>>
<<eatFood 200 20>>
<<gainStat "energy" 5>>
<<gainCharacterStat "dinerChef" "friendship" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>><<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _v = random(1, 3)>>
<</silently>>
<<if _v === 1>>
<<narrative>>
The rack is full and another bus tub just came in. You're falling behind.
Mike finishes his side, looks at your pile, and walks over. He starts unloading without a word.
<</narrative>>
<<narrative>>
You work side by side for a few minutes. The pile goes down fast. He goes back to his station when it's done.
<</narrative>>
<<elseif _v === 2>>
<<narrative>>
Mike glances over during a rush. Your station is overflowing. He dries his hands, walks over, and starts stacking the clean ones onto the rack.
<</narrative>>
<<dialog "dinerDishwasher">>
You rinse. I'll stack.
<</dialog>>
<<narrative>>
No argument. You get through the backlog in half the time.
<</narrative>>
<<else>>
<<narrative>>
You're scrubbing a burned pot that won't come clean. Mike reaches over and takes it from you.
<</narrative>>
<<dialog "dinerDishwasher">>
Use the steel wool. And soak it first next time.
<</dialog>>
<<narrative>>
He scrubs it clean in thirty seconds. Hands it back, already looking elsewhere.
<</narrative>>
<</if>>
<<silently>>
<<gainStat "energy" 8>>
<<gainCharacterStat "dinerDishwasher" "friendship" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>/* ==========================================
Opens like quest_vince_day3_family_reflection: fhBedroom finishes,
then Engine.play (fhBedroomPlayPassage). Requires dianaEventShown && !dianaThoughtsShown.
Voice: <<narrative>> to player (*you* ok); inner = dialog (Inner voice), *I*.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhBedroom">>
<<advanceTime 25>>
<</silently>>
<<narrative "Bedroom">>
You closed the door behind you.
You didn't sit down. You just fell onto the bed and stared at the ceiling.
<</narrative>>
<<vid "assets/content/scenes/events/diana/nightBedThink.mp4" "100%">>
<<narrative>>
The perfume stuck with you. And that voice "Hey, babe."
<</narrative>>
<<narrative>>
Vince's face. The way it changed when he saw her.
<</narrative>>
<<narrative>>
Then his hand.
<</narrative>>
<<narrative>>
The men saw it too. All of them.
<</narrative>>
<<narrative>>
"You could never get enough of that." "I wonder what she's like in bed." "I'd go all night..."
<</narrative>>
<<narrative>>
Nobody stopped it. Nobody flinched. Like it was the only thing worth talking about.
<</narrative>>
<<dialog "player">>
(Inner voice)
When I walk by, do they talk about me like that?
<</dialog>>
<<narrative>>
You pause on that thought.
<</narrative>>
<<narrative>>
You're a woman too. You work in that kitchen every day. They see you too.
<</narrative>>
<<dialog "player">>
(Inner voice)
What do they say about me?
Maybe I don't want to know.
<</dialog>>
<<narrative>>
You lie there and try not to imagine itthen imagine it anyway. A laugh you can't quite hear. A nickname that isn't yours.
<</narrative>>
<<narrative>>
Your stomach tightens. You tell yourself you're being stupid. It doesn't listen.
<</narrative>>
<<dialog "player">>
(Inner voice)
It shouldn't matter this much.
<</dialog>>
<<narrative>>
It still does.
<</narrative>>
<<narrative>>
Emma's face comes to mind.
<</narrative>>
<<narrative>>
That look on her face. Not just anger something older than that. The way she wiped that booth, how she dropped the plates on the counter, how she didn't smile back at Diana.
<</narrative>>
<<dialog "player">>
(Inner voice)
Jealous? Maybe. Not just that.
Does she know Diana? Or is it all because of Vince?
I need to know.
I need to talk to Emma.
<</dialog>>
<<narrative>>
Enough.
<</narrative>>
<<narrative>>
You sat up.
<</narrative>>
<<nobr>>
<<set $flags.dianaGossipUnlocked = true>>
<<set $flags.dianaThoughtsShown = true>>
<<startQuest "something_different">>
<</nobr>>
<div class="location-actions">
<<btn "Get up" "fhBedroom">><</btn>>
</div>
</div><div class="divnobr">
<<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<</if>>
<<if !$_navigatingBackward>>
<<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _v = random(1, 2)>>
<</silently>>
<<if _v === 1>>
<<narrative "Rush">>
Three bus tubs hit your station at once. The kitchen is loud, everyone's moving, and your sink is already full. Plates stacked on plates.
You look at the pile. This is going to take everything you've got or you could call Mike over.
<</narrative>>
<<else>>
<<narrative "Rush">>
A party of twelve just left. All at once. The entire table's worth of dishes lands on your counter in two loads. The stack is taller than the tap.
You've got two options dig in and power through, or wave Mike over before it gets worse.
<</narrative>>
<</if>>
<div class="location-actions">
<<if _v === 1>>
<<btn "Push through it alone" "dinerWork_event_rushSurge_pushThrough">><</btn>>
<<btn "Signal Mike for help" "dinerWork_event_rushSurge_askHelp">><</btn>>
<<else>>
<<btn "Handle it yourself" "dinerWork_event_rushSurge_pushThrough">><</btn>>
<<btn "Get Mike's attention" "dinerWork_event_rushSurge_askHelp">><</btn>>
<</if>>
</div>
<</if>>
</div><div class="divnobr">
<<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<</if>>
<<if !$_navigatingBackward>>
<<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _charisma = $charisma || 0>>
<<set _roll = random(1, 100)>>
<<set _threshold = 30 + Math.min(_charisma, 40)>>
<</silently>>
<<if _roll <= _threshold>>
<<narrative>>
You catch Mike's eye and tilt your head toward the pile. He nods.
Two people, twice the speed. The rush clears faster than it built up.
<</narrative>>
<<silently>>
<<gainCharacterStat "dinerDishwasher" "friendship" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<else>>
<<narrative>>
You try to get Mike's attention but he's buried in his own stack. He doesn't see you or pretends not to.
You handle it alone. It takes longer than it should.
<</narrative>>
<<silently>>
<<loseStat "energy" 5>>
<<loseStat "mood" 3>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<</if>>
<<include "RubysDishwashWork_afterMini">>
<</if>>
</div><div class="divnobr">
<<silently>>
<<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<</if>>
<<if !$_navigatingBackward>>
<<set $location = $activityOrigin || "dinerRubys">>
<</silently>>
<<narrative>>
You don't look up. Hands in the water, one plate at a time. The pile doesn't shrink for a while. Then it does.
Your arms are heavy by the end. But you did it.
<</narrative>>
<<silently>>
<<loseStat "energy" 8>>
<<gainStat "stress" 5>>
<<gainSkill "practical" "dishwashing" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<include "RubysDishwashWork_afterMini">>
<</if>>
</div><<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _v = random(1, 3)>>
<</silently>>
<<if _v === 1>>
<<narrative>>
Sofia backs through the kitchen door with a stack of plates. She sets them on the edge of your station and stays a second longer than she needs to.
<</narrative>>
<<dialog "dinerWaitress3">>
Table six sent back the pasta again. Third time this week. James is going to lose it.
<</dialog>>
<<narrative>>
She shakes her head, half-smiling, and heads back out.
<</narrative>>
<<elseif _v === 2>>
<<narrative>>
Sofia drops off a load of glasses and leans against the counter for a moment.
<</narrative>>
<<dialog "dinerWaitress3">>
How do you do this all day? I'd go crazy back here.
<</dialog>>
<<narrative>>
She doesn't wait for an answer. Taps the counter twice and walks off. But it was nice that someone asked.
<</narrative>>
<<else>>
<<narrative>>
Sofia comes in to grab napkins from the shelf. She glances at your station.
<</narrative>>
<<dialog "dinerWaitress3">>
You're getting faster at this, you know.
<</dialog>>
<<narrative>>
She says it casually, like it's nothing. It's not nothing.
<</narrative>>
<</if>>
<<silently>>
<<gainStat "mood" 5>>
<<loseStat "stress" 3>>
<<gainCharacterStat "dinerWaitress3" "friendship" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>><<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _v = random(1, 3)>>
<</silently>>
<<if _v === 1>>
<<narrative>>
Tom comes to the back for his break. Leans against the shelf near your station, arms crossed.
<</narrative>>
<<dialog "dinerClerk">>
Vince changed the schedule again. Third time this month. Swear he does it just to mess with people.
<</dialog>>
<<narrative>>
He talks for a minute. You listen while you scrub. He heads back out looking slightly less annoyed.
<</narrative>>
<<elseif _v === 2>>
<<narrative>>
Tom wanders into the back with a coffee. Stands near you, not quite talking, not quite silent.
<</narrative>>
<<dialog "dinerClerk">>
You ever notice how the lunch crowd is worse than dinner? Dinner people at least know what they want.
<</dialog>>
<<narrative>>
You nod. He sips his coffee. That's the whole conversation.
<</narrative>>
<<else>>
<<narrative>>
Tom pushes through the kitchen door, phone in hand, on break.
<</narrative>>
<<dialog "dinerClerk">>
Did you see the game last night?
<</dialog>>
<<narrative>>
You didn't. He tells you about it anyway the short version. It's a nice two minutes of not thinking about dishes.
<</narrative>>
<</if>>
<<silently>>
<<loseStat "stress" 3>>
<<gainStat "mood" 3>>
<<gainCharacterStat "dinerClerk" "friendship" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>><<silently>>
<<set $location = $activityOrigin || "dinerRubys">>
<<set _dishSkill = ($skills && $skills.practical && $skills.practical.dishwashing) ? $skills.practical.dishwashing : 0>>
<<set _v = random(1, 2)>>
<</silently>>
<<if _dishSkill >= 20>>
<<if _v === 1>>
<<narrative>>
Vince walks through the kitchen. He stops behind you for a second. You don't turn around.
He looks at the rack, the station, the floor. Then he leaves.
<</narrative>>
<<narrative>>
No comment. From Vince, that's a compliment.
<</narrative>>
<<else>>
<<narrative>>
Vince is doing his rounds. He pauses at your station. Looks at the organized rack, the clean counter.
<</narrative>>
<<dialog "dinerManager">>
Good.
<</dialog>>
<<narrative>>
One word. He moves on. You feel slightly better about the shift.
<</narrative>>
<</if>>
<<silently>>
<<gainStat "mood" 3>>
<<gainCharacterStat "dinerManager" "friendship" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<else>>
<<if _v === 1>>
<<narrative>>
Vince appears behind you. You didn't hear him come in.
<</narrative>>
<<dialog "dinerManager">>
You're leaving water marks on the glasses. Rinse them properly.
<</dialog>>
<<narrative>>
He taps the rack and walks off. You rinse the next batch twice.
<</narrative>>
<<else>>
<<narrative>>
Vince leans into the kitchen doorway. His eyes go straight to the pile on your counter.
<</narrative>>
<<dialog "dinerManager">>
That shouldn't still be sitting there.
<</dialog>>
<<narrative>>
He doesn't wait for an explanation. You move faster.
<</narrative>>
<</if>>
<<silently>>
<<gainStat "stress" 3>>
<<loseStat "mood" 2>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<</if>>/* Vince day 3 arc, all passages in Quests/vinceDay3/. One-shot flag set in RubysDishwashWork before goto. */
<div class="divnobr">
<<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<</if>>
<<if !$_navigatingBackward>>
<<set $location = "dinerRubysKitchen">>
<<updateCharacterLocations>>
<<loseStat "mood" 10>>
<<gainStat "stress" 5>>
<<narrative "Kitchen">>
You're peeling off your gloves when Vince walks into the kitchen.
<</narrative>>
<<narrative>>
Nobody notices him at first. He goes straight to James, leans in, and murmurs something in his ear.
<</narrative>>
<<narrative>>
James nods. A quick, familiar smile passes between them.
<</narrative>>
<<narrative>>
Then he moves on to Mike. Mike's got headphones on, lost in his music.
<</narrative>>
<<narrative>>
Vince touches his shoulder, light. Mike startles, lifts a hand without quite pulling the headphones off, and flashes an okay sign.
<</narrative>>
<<narrative>>
Vince returns it the same way. Quick. Routine.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/day3/day3vincemike.webp" "100%">>
<<narrative>>
Then Vince turns to you. He pauses a beat.
<</narrative>>
<<narrative>>
His eyes move over you. Fast. Not flat enough to miss.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/day3/day3vincetalk.webp" "100%">>
<<dialog "dinerManager">>
How's it going?
<</dialog>>
<<narrative>>
A short gap.
<</narrative>>
<<dialog "dinerManager">>
Settling in?
<</dialog>>
<<dialog "player">>
Good…
I'm st..
<</dialog>>
<<dialog "dinerWaitress1">>
(from the pass)
James! Is four up? The guy's complaining again!
<</dialog>>
<<narrative>>
Vince's attention snaps away. He turns his head; his shoulders loosen.
<</narrative>>
<<narrative>>
He walks off from you without a word.
<</narrative>>
<<narrative>>
He takes a few steps toward Emma, then stops.
<</narrative>>
<<narrative>>
His eyes skim her face for a second.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/day3/day3vinceemma.webp" "100%">>
<<dialog "dinerManager">>
You changed your lipstick.
<</dialog>>
<<dialog "dinerWaitress1">>
So you noticed.
<</dialog>>
<<dialog "dinerManager">>
Hard not to notice what suits you.
<</dialog>>
<<narrative>>
A brief silence follows.
<</narrative>>
<<narrative>>
The question he asked you hangs unfinished, like there won't be a follow-up.
<</narrative>>
<<narrative>>
Maybe that's how it is, you think. They've worked together long enough.
<</narrative>>
<<narrative>>
They keep talking; you can't make out the words.
<</narrative>>
<<narrative>>
But you see Emma smile, and you see Vince's eyes not leave her.
<</narrative>>
<<narrative>>
You look at Emma again. She stands easy, like she belongs exactly where she is.
<</narrative>>
<<narrative>>
Her lipstick is flawless. Her face a little too put together.
<</narrative>>
<<narrative>>
You look a second longer. Something snags in your mind.
<</narrative>>
<<narrative>>
Do you ever look at yourself in the mirror?
<</narrative>>
<<narrative>>
You hadn't thought much of it at the time. Now it... won't go away.
<</narrative>>
<<narrative>>
You dip your head slightly. Your silhouette shows in the shine of the counter.
<</narrative>>
<<narrative>>
You look for a moment.
<</narrative>>
<<narrative>>
Then Vince claps, and you flinch.
<</narrative>>
<<dialog "dinerManager">>
All right, everyone, let's keep moving. We're doing good.
<</dialog>>
<<narrative>>
Vince turns and walks out of the kitchen.
<</narrative>>
<<silently>>
<<set $flags.vinceDay3FamilyOpen = true>>
<<startQuest "vince_day3_family">>
<</silently>>
<div class="location-actions">
<<btn "Start your shift" "RubysDishwashWork">><</btn>>
</div>
<</if>>
</div>
<<nobr>>
<<silently>>
<<set _ret = $hubAmbientReturn || "maplewood">>
<<set _d = $hubAmbientDistrict || $location>>
<<set $location = _d>>
<<set _mins = parseInt($selectedDuration, 10) || 10>>
<<set _phoneEnergy = Math.max(1, Math.round(8 * _mins / 60))>>
<<set _needHubEnergy = _phoneEnergy + 10>>
<<requireMinEnergy _needHubEnergy _ret "Dont have enought energy">>
<<hubAmbientPickFlavor "phone">>
<<set _phoneClipIds = [1, 2, 3, 5, 6, 7]>>
<<set _districtHubVid = "assets/content/scenes/global/districtHub/phone" + _phoneClipIds[random(0, 5)] + ".mp4">>
<<advanceTime _mins "relax">>
<<set _hubStressLoss = Math.max(1, Math.round(4 * _mins / 60))>>
<<set _hubMoodRate = (setup.hubAmbientMoodGainPerHour && setup.hubAmbientMoodGainPerHour.phone != null) ? setup.hubAmbientMoodGainPerHour.phone : 8>>
<<set _hubMoodGain = Math.max(1, Math.round(_hubMoodRate * _mins / 60))>>
<<loseStat "stress" _hubStressLoss>>
<<gainStat "mood" _hubMoodGain>>
<<loseStat "energy" _phoneEnergy>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative "Phone">>
<<print _flavor>>
<</narrative>>
<<if _districtHubVid !== "">><<vid _districtHubVid "100%">><</if>>
<div class="location-actions">
<<btn "Back" _ret>><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _ret = $hubAmbientReturn || "maplewood">>
<<set _d = $hubAmbientDistrict || $location>>
<<set $location = _d>>
<<set _mins = parseInt($selectedDuration, 10) || 15>>
<<set _walkEnergy = Math.max(1, Math.round(10 * _mins / 60))>>
<<set _needHubEnergy = _walkEnergy + 10>>
<<requireMinEnergy _needHubEnergy _ret "Dont have enought energy">>
<<hubAmbientPickFlavor "walk">>
<<set _districtHubVid = "assets/content/scenes/global/districtHub/walking" + random(1, 5) + ".mp4">>
<<advanceTime _mins "relax">>
<<set _hubStressLoss = Math.max(1, Math.round(6 * _mins / 60))>>
<<set _hubMoodGain = Math.max(1, Math.round(8 * _mins / 60))>>
<<loseStat "stress" _hubStressLoss>>
<<gainStat "mood" _hubMoodGain>>
<<loseStat "energy" _walkEnergy>>
<<addHeelsTravelMinutes _mins>>
<<recalculateStats>>
<<flushNotifications>>
<<set _walkAroundDiscoverStoreBook = false>>
<<if _d === "oldTown" && !$discoveredStoreBook && random(1, 4) === 1>>
<<set _walkAroundDiscoverStoreBook = true>>
<</if>>
<</silently>>
<<narrative "Walk Around">>
<<print _flavor>>
<</narrative>>
<<if _districtHubVid !== "">><<vid _districtHubVid "100%">><</if>>
<<if _walkAroundDiscoverStoreBook>>
<<narrative>>
While you're taking everything in, a storefront suddenly snags your attentioncrowded window, paperbacks stacked like brickwork, a little handwritten sign about bargain bins.
<</narrative>>
<<image "assets/content/locations/oldTown/storeBook.webp" "100%">>
<<dialog "player">>
(Inner voice)
A real bookshop tucked into Old Town. I wonder what's actually in therebesides dust and good intentions.
<</dialog>>
<<narrative>>
You discovered Bookworm's Corner.
<</narrative>>
<<silently>>
<<discover "StoreBook" "Bookworm's Corner">>
<</silently>>
<</if>>
<div class="location-actions">
<<btn "Back" _ret>><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _ret = $hubAmbientReturn || "maplewood">>
<<set _d = $hubAmbientDistrict || $location>>
<<set $location = _d>>
<<set _mins = parseInt($selectedDuration, 10) || 15>>
<<set _watchEnergy = Math.max(1, Math.round(10 * _mins / 60))>>
<<set _needHubEnergy = _watchEnergy + 10>>
<<requireMinEnergy _needHubEnergy _ret "Dont have enought energy">>
<<hubAmbientPickFlavor "watch">>
<<set _districtHubVid = "assets/content/scenes/global/districtHub/watchPeople" + random(1, 6) + ".mp4">>
<<advanceTime _mins "relax">>
<<set _hubStressLoss = Math.max(1, Math.round(8 * _mins / 60))>>
<<set _hubMoodGain = Math.max(1, Math.round(6 * _mins / 60))>>
<<loseStat "stress" _hubStressLoss>>
<<gainStat "mood" _hubMoodGain>>
<<loseStat "energy" _watchEnergy>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative "People Watching">>
<<print _flavor>>
<</narrative>>
<<if _districtHubVid !== "">><<vid _districtHubVid "100%">><</if>>
<div class="location-actions">
<<btn "Back" _ret>><</btn>>
</div>
<</nobr>><<nobr>><<set $location = "dormsUni">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Wellington Hall">>The student housing complex. Life, laughter, and late-night studying.<</narrative>>
<<showLocationChars "dormsUni">>
<<navMenu>><<navCard "universityDistrict">><</navMenu>><<nobr>>
<<set $location = "downTown">>
<<updateCharacterLocations>>
<<if ndef $flags>><<set $flags = {}>><</if>>
<<set _goToMallActive = $questState.active.go_to_mall>>
<<set _firstDowntown = !$flags.firstDowntownVisit>>
<<set _atVisitStage = _goToMallActive && _goToMallActive.stage === 0>>
<<if _firstDowntown && _atVisitStage>>
<<include "quest_go_to_mall_downtown_first">>
<<else>>
<<narrative "Downtown">>
The bustling heart of the city. Towering skyscrapers, busy streets, and endless opportunities await. The sound of traffic and city life fills the air.
<</narrative>>
<<showLocationChars "downtown">>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "cityHall">>
<<navCard "nightClub">>
<<navCard "restaurantDowntown">>
<<navCard "mall">>
<<navCard "skyline">>
<<navCard "maplewood">>
<<navCard "oldTown">>
<<navCard "marinaBay">>
<<navCard "redLightCenter">>
<</navMenu>>
<</if>>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "fhKitchen">>
<<gainStat "energy" 25>>
<<loseStat "stress" 5>>
<<flushNotifications>>
<<advanceTime 10>>
<<recalculateStats>>
<</silently>>
<<narrative "Coffee">>
You brew a fresh cup and take a moment at the counter. The warmth in your hands and the familiar smell help you pause. You sip slowly, feeling a little more alert and a bit less wound up.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/kitchen/coffee.mp4" "100%">>
<div class="location-actions">
<<btn "Finish" "fhKitchen">><</btn>>
</div>
<</nobr>><<silently>>
<<set $location = "fhKitchen">>
<<advanceTime 5>>
<<loseStat "thirst" 100>>
<<run window.notifySuccessIfStatsEnabled('thirst', "Thirst Quenched")>>
<<flushNotifications>>
<</silently>>
<<narrative "Drink Water">>
You grab a glass from the cupboard and fill it with cool, refreshing water. You drink it down, feeling satisfied.
<</narrative>>
<<vid "assets/content/scenes/maplewood/FamilyHouse/kitchen/drinkWater.mp4" "100%">>
<div class="location-actions">
<<btn "Finish" "fhKitchen">><</btn>>
</div><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<<if ndef $prologueSelections>>
<<set $prologueSelections = {
early: null,
childhood: null,
formative: null,
adolescent: null,
coming: null
}>>
<<set $prologueBonuses = {}>>
<</if>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<!-- Sidebar -->
<div class="prologue-sidebar" id="prologue-sidebar">
<div class="prologue-sidebar-title">My Selections</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title current">Early Years (0-5)</div>
<<if $prologueSelections.early>>
<div class="prologue-sidebar-item">
<div>
<div class="prologue-sidebar-item-text">$prologueSelections.early.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.early.effect</div>
</div>
</div>
<<else>>
<div class="prologue-sidebar-empty">No selection yet</div>
<</if>>
</div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Childhood (6-9)</div></div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Formative (10-12)</div></div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Adolescent (13-15)</div></div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Coming of Age (16-17)</div></div>
</div>
<div class="prologue-content-area">
<!-- Progress Bar -->
<div class="prologue-progress-container">
<div class="prologue-progress-bar">
<div class="prologue-progress-fill" style="width: 16.6%;"></div>
</div>
<div class="prologue-progress-steps">
<span class="prologue-progress-step active">0-5</span>
<span class="prologue-progress-step">6-9</span>
<span class="prologue-progress-step">10-12</span>
<span class="prologue-progress-step">13-15</span>
<span class="prologue-progress-step">16-17</span>
</div>
</div>
<div class="prologue-header">
<div class="prologue-age-badge">Ages 0-5</div>
<h1 class="prologue-title">First Memories</h1>
<div class="prologue-header-line"></div>
<p class="prologue-subtitle">The world was small, but it was yours...</p>
</div>
<<image "assets/content/scenes/prologue/earlyYears.webp" "100%">>
<<narrative>>
The small town where you were born felt endless back then. Every street, every neighbor, every corner of the backyardthat was your entire world. Days were simple. Mom at home, dad at work, the warm smell of dinner filling the house each evening.
<</narrative>>
<<narrative>>
You were two when your brother was born.
<</narrative>>
<<narrative>>
When they came back from the hospital, everything changed. Mom didn't have as much time for you anymore. She was constantly busy with the babyfeeding, putting him to sleep, calming him when he cried. "Wait a bit, sweetie, I need to take care of your brother first," she'd said one day. You never forgot that sentence.
<</narrative>>
<<narrative>>
When dad came home from work, he didn't throw you in the air anymore. He'd hold the baby, smile at him, talk to him. You stood to the side, watching.
<</narrative>>
<<narrative>>
But one evening was different. Dad came home from work and went straight to you. "Come on, big girl, I want to show you something." He took you to the backyard, showed you the small bicycle he'd just boughtyour size. "This is just for you," he said. "Not for your brother. Yours."
<</narrative>>
<<narrative>>
That bicycle changed everything.
<</narrative>>
<<narrative>>
As your brother grew, you started finding your place. You weren't just "mom and dad's daughter" anymoreyou were "his big sister" too. When he fell, you ran. When he cried, you comforted him. "Don't cry, I'm here," you'd said one day in the backyard. He looked up at you, tears still on his cheeks, and smiled.
<</narrative>>
<<narrative>>
Mom saw from the kitchen. "Look at her," she said softly to dad. "She's growing up."
<</narrative>>
<<narrative>>
There was pride in dad's eyes.
<</narrative>>
<<narrative>>
In that moment, something clicked. This was your placebetween them. Mom and dad's big girl. Your brother's big sister. Part of the family, but now you had your own place.
<</narrative>>
<<narrative>>
Those early years shaped you more than you knew.
<</narrative>>
<div class="prologue-memory-section">
<div class="prologue-memory-section-title">Character-Shaping Memories</div>
<div class="prologue-memory-cards" id="early-cards">
<div class="prologue-memory-card" data-section="early" data-title="Your Own Space" data-effect="+15% Focus" data-stat="focus" data-value="1.15">
<div class="prologue-memory-title">Your Own Space</div>
<div class="prologue-memory-text">
"Everyone fussed over your brother. That meant you got the corner. You'd stack blocks until the tower came down, then stack them again. Mom would check in. 'You okay, sweetie?' Dad would ruffle your hair on his way past. When your brother turned three, he started sitting next to you, watching. Dad noticed. 'Good job teaching him.' You didn't think you were teaching anyone anything. You just liked having him there."
</div>
<div class="prologue-memory-effect">+15% Focus</div>
</div>
<div class="prologue-memory-card" data-section="early" data-title="Dad's Kingdom" data-effect="+15% Fitness" data-stat="fitness" data-value="1.15">
<div class="prologue-memory-title">Dad's Kingdom</div>
<div class="prologue-memory-text">
"The backyard was dad's kingdom, and he made it yours too. Every weekend: 'Come on, let's go outside.' Climbing trees, kicking the ball until the streetlights came on. When your brother started walking, you'd take his hand. 'Come on, I'll show you.' Dad watched from the porch, coffee in hand, smiling. 'She's got your energy,' mom would say. He had that energy all his life. You recognized it in yourself before you could name it."
</div>
<div class="prologue-memory-effect">+15% Fitness</div>
</div>
<div class="prologue-memory-card" data-section="early" data-title="Mom's Special Time" data-effect="+15% Intelligence" data-stat="intelligence" data-value="1.15">
<div class="prologue-memory-title">Mom's Special Time</div>
<div class="prologue-memory-text">
"Every night, mom would read to both of you. But after your brother fell asleep, she'd stay. 'This is our time,' she'd whisper, and keep reading. Just for you. 'You're my first,' she'd say sometimes, tucking you in. 'That means something.' You still remember the weight of her voice going quiet at the end of a chapter. Those twenty minutes were yours. Nothing else felt like that."
</div>
<div class="prologue-memory-effect">+15% Intelligence</div>
</div>
<div class="prologue-memory-card" data-section="early" data-title="Family Pride" data-effect="+15% Charisma" data-stat="charisma" data-value="1.15">
<div class="prologue-memory-title">Family Pride</div>
<div class="prologue-memory-text">
"There was always a game happening on the street. You'd gather the group, explain the rules, sort out who was on which team. When your brother turned three, you'd bring him along. 'This is my brother,' you'd announce proudly. He was the youngest on the block. Mom watched from the window. 'Look at her, taking care of him.' Dad would come out sometimes and join the game. 'That's my girl.' You didn't know you were in charge. You just got everyone to stop arguing about rules so the game could start."
</div>
<div class="prologue-memory-effect">+15% Charisma</div>
</div>
</div>
</div>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-secondary">Back</span>' "prologuePage">><</link>>
<span id="early-continue" class="prologue-btn prologue-btn-primary disabled">Continue</span>
</div>
</div>
</div>
</div>
</div>
<</nobr>>
<<script>>
$(document).ready(function() {
// Handle card selection
$('#early-cards .prologue-memory-card').on('click', function() {
var $card = $(this);
var section = $card.data('section');
var title = $card.data('title');
var effect = $card.data('effect');
var stat = $card.data('stat');
var value = $card.data('value');
// Remove selected from siblings
$card.siblings().removeClass('selected');
$card.addClass('selected');
// Store selection with stat data for multiplier application
State.variables.prologueSelections[section] = {
title: title,
effect: effect,
stat: stat,
value: value
};
// Enable continue button
$('#early-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('childhoodYears');
});
// Update sidebar - find the current section
var $currentSection = $('.prologue-sidebar-section-title.current').parent();
// Remove empty placeholder if exists
$currentSection.find('.prologue-sidebar-empty').remove();
// Remove existing item if re-selecting
$currentSection.find('.prologue-sidebar-item').remove();
// Add new selection
$currentSection.append(
'<div class="prologue-sidebar-item"><div>' +
'<div class="prologue-sidebar-item-text">' + title + '</div>' +
'<div class="prologue-sidebar-item-effect">' + effect + '</div>' +
'</div></div>'
);
});
// Re-select if already chosen
var existing = State.variables.prologueSelections.early;
if (existing) {
$('#early-cards .prologue-memory-card').each(function() {
if ($(this).data('title') === existing.title) {
$(this).addClass('selected');
$('#early-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('childhoodYears');
});
}
});
}
});
<</script>>
<<silently>>
<<set $location = "fhKitchen">>
<<eatFood 750 100>>
<<loseStat "thirst" 100>>
<<gainStat "energy" 45>>
<<gainStat "mood" 15>>
<<gainStat "health" 2>>
<<loseStat "stress" 7>>
<<advanceTime 30 "relax">>
<<run window.notifyKitchenEatFoodToast()>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative "Eat Food">>
You prepare a quick meal and sit down to eat. The food is warm and filling, satisfying your hunger.
Taking your time, you enjoy the meal, feeling your energy return as your body processes the nutrients.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/kitchen/eatFood.mp4" "50%">>
<div class="location-actions">
<<btn "Finish" "fhKitchen">><</btn>>
</div><<silently>>
<<set $location = "fhKitchen">>
/* Who is at the table (mood bonus scales with present family) */
<<set _presentFamily = []>>
<<if $characters.mother.currentLocation is "fhKitchen">>
<<run _presentFamily.push("mother")>>
<</if>>
<<if $characters.father.currentLocation is "fhKitchen">>
<<run _presentFamily.push("father")>>
<</if>>
<<if $characters.brother.currentLocation is "fhKitchen">>
<<run _presentFamily.push("brother")>>
<</if>>
<<eatFood 750 100>>
<<loseStat "thirst" 100>>
<<gainStat "bladder" 15>>
<<gainStat "energy" 45>>
<<loseStat "stress" 12>>
<<set _familyMoodExtra = Math.min(9, _presentFamily.length * 3)>>
<<gainStat "mood" (15 + _familyMoodExtra)>>
<<gainStat "health" 5>>
/* Determine meal type based on time */
<<if $timeSys.hour >= 7 and $timeSys.hour < 9>>
<<set _mealType = "breakfast">>
<<set _mealTitle = "Family Breakfast">>
<<set $familyMeals.breakfast = true>>
<<elseif $timeSys.hour >= 12 and $timeSys.hour < 14>>
<<set _mealType = "lunch">>
<<set _mealTitle = "Lunch">>
<<set $familyMeals.lunch = true>>
<<else>>
<<set _mealType = "dinner">>
<<set _mealTitle = "Family Dinner">>
<<set $familyMeals.dinner = true>>
<</if>>
/* Relationship bonus - for each present family member */
<<for _member range _presentFamily>>
<<gainCharacterStat _member "friendship" 1 true>>
<<gainCharacterStat _member "love" 1 true>>
<<set _memberName = (setup.characterDefs[_member] && setup.characterDefs[_member].firstName) ? setup.characterDefs[_member].firstName : (($characters[_member] && ($characters[_member].firstName || $characters[_member].name)) ? ($characters[_member].firstName || $characters[_member].name) : _member)>>
<<run window.notifySuccess(_memberName + " Friendship & Love +1")>>
<</for>>
<<advanceTime 30 "relax">>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative _mealTitle>>
<<nobr>>
<<if _mealType is "breakfast">>
The morning sun streams through the kitchen window as you sit down for breakfast with your family.
<<elseif _mealType is "lunch">>
You gather around the table for a midday meal.
<<else>>
The kitchen fills with warmth as the family comes together for dinner.
<</if>>
<</nobr>>
<</narrative>>
<<nobr>>
/* Dialogs based on present characters */
<<if _presentFamily.includes("mother")>>
<<dialog "mother">>
<<if _mealType is "breakfast">>
Good morning, sweetie! Did you sleep well?
<<elseif _mealType is "lunch">>
I made your favorite today. Sit down and eat while it's hot.
<<else>>
How was your day? Tell me everything while we eat.
<</if>>
<</dialog>>
<</if>>
<<if _presentFamily.includes("father")>>
<<dialog "father">>
<<if _mealType is "breakfast">>
(sipping coffee) So, any plans for today?
<<elseif _mealType is "lunch">>
(taking a bite) Your mother's cooking is the best.
<<else>>
It's nice when we can all eat together like this.
<</if>>
<</dialog>>
<</if>>
<<if _presentFamily.includes("brother")>>
<<dialog "brother">>
<<if _mealType is "breakfast">>
(yawning) Morning... Is there any coffee left?
<<elseif _mealType is "lunch">>
Can you pass the salt?
<<else>>
(eating quickly) This is good!
<</if>>
<</dialog>>
<</if>>
<</nobr>>
<<narrative>>
<<nobr>>
You enjoy the meal together, the conversation flowing easily between bites.
<<if _presentFamily.length >= 2>>
It feels good to share this moment with your family.
<<else>>
Even a simple meal feels better when shared with someone.
<</if>>
<</nobr>>
<</narrative>>
<div class="location-actions">
<<btn "Finish" "fhKitchen">><</btn>>
</div>
<<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<logDailyActivity "dinerWaitress1" "talk">>
<<set _char = $characters.dinerWaitress1>>
<<set _stats = $characters.dinerWaitress1.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase dishwasherOnBreak: worked >0h but <8h today; dishwasherDone: full 8h done within last 3h */
<<set _phase = "common">>
<<if $job && $job.id === "ruby_dishwasher" && def $jobState>>
<<if $jobState.hoursToday >= 8>>
<<if $jobState.lastShiftEndHour !== null && ($timeSys.hour - $jobState.lastShiftEndHour) <= 3>>
<<set _phase = "dishwasherDone">>
<</if>>
<<elseif $jobState.hoursToday > 0>>
<<set _phase = "dishwasherOnBreak">>
<</if>>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Back rooms: exact location first (per-room DB), then dinerRubys_* (front-of-house keys) */
<<set _isDinerBackArea = ($location === "dinerRubysKitchen" || $location === "dinerRubysStorage" || $location === "dinerRubysCorridor" || $location === "dinerRubysBathroom" || $location === "dinerRubysDressingRoom")>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.emmaTopics && setup.emmaTopics[_phase] && setup.emmaTopics[_phase][_levelKey] && setup.emmaTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.emmaTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _ctxKey = "dinerRubys_" + _phase + "_" + _timeSlot>>
<<if setup.emmaTopics && setup.emmaTopics[_phase] && setup.emmaTopics[_phase][_levelKey] && setup.emmaTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.emmaTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.emmaTopics && setup.emmaTopics.common && setup.emmaTopics.common[_levelKey] && setup.emmaTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.emmaTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _fallbackKey = "dinerRubys_common_" + _timeSlot>>
<<if setup.emmaTopics && setup.emmaTopics.common && setup.emmaTopics.common[_levelKey] && setup.emmaTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.emmaTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
/* Pick topic if pool exists */
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "Emma is too busy to talk right now.", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "dinerWaitress1" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "dinerWaitress1" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "dinerWaitress1" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "dinerWaitress1" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerWaitress1.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>>/* ==========================================
EMMA TALK - Diana Gossip (one-time)
Unlocks after dianaThoughtsShown.
Opens Sofia gossip when complete.
Voice: narrative = external (you); inner voice = first-person self-talk.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<logDailyActivity "Talked to Emma">>
<<gainCharacterStat "dinerWaitress1" "friendship" 2>>
<<advanceTime 20>>
<</silently>>
<<image "assets/content/scenes/interactions/emma/level1/emmaFrontMorning-1.webp" "100%">>
<<dialog "player">>
Hey Emma. How are you?
<</dialog>>
<<dialog "dinerWaitress1">>
Fine. Why are you asking?
<</dialog>>
<<dialog "player">>
You seemed pretty annoyed yesterday. You okay?
<</dialog>>
<<dialog "dinerWaitress1">>
(laughs)
I'm always annoyed. Just now noticing?
<</dialog>>
<<dialog "player">>
The way you looked when Diana left... Did something happen between you and Vince? Work thing?
<</dialog>>
<<narrative>>
Emma stopped. She set the rag down on the counter.
<</narrative>>
<<dialog "dinerWaitress1">>
What does that have to do with anything.
<</dialog>>
<<dialog "player">>
Looked like it did.
<</dialog>>
<<dialog "dinerWaitress1">>
Vince is our boss. Sometimes you get along, sometimes you don't. That's it.
<</dialog>>
<<dialog "player">>
Because of Diana?
<</dialog>>
<<narrative>>
A short silence.
<</narrative>>
<<dialog "dinerWaitress1">>
When women like that show up, Vince becomes someone else. Loses focus, changes his mind about things. There was this one time... forget it. It's done.
<</dialog>>
<<dialog "player">>
What happened that time?
<</dialog>>
<<dialog "dinerWaitress1">>
(sharp)
I said forget it.
<</dialog>>
<<narrative>>
She picked up the rag. Turned around. But kept talking.
<</narrative>>
<<dialog "dinerWaitress1">>
You know what gets me? He's a smart guy. Could be a good boss. But when someone like that walks in, brain shuts off. Like every man.
<</dialog>>
<<dialog "dinerWaitress1">>
Why? Because she's pretty. Because she knows how to dress, how to wear perfume, how to walk. She's decoration for Vince. He puts her on his arm, grabs her ass, shows her off to everyone. And she knows her role she's there for the money, the comfort. They both know the game.
<</dialog>>
<<dialog "dinerWaitress1">>
But men only see that walk. That perfume. The rest is gone.
<</dialog>>
<<dialog "player">>
So what's Diana's fault in all this?
<</dialog>>
<<dialog "dinerWaitress1">>
(pausing)
She doesn't have one. She knows the game. Like every woman.
<</dialog>>
<<narrative>>
A short silence.
Her voice changed.
<</narrative>>
<<dialog "dinerWaitress1">>
I used to take care of myself. Back then.
<</dialog>>
<<narrative>>
She said it and moved on. Didn't dwell on it.
<</narrative>>
<<dialog "dinerWaitress1">>
You're young. Right now it feels like it doesn't matter. I get it. Everyone thinks that at your age you've got time, everything's ahead of you.
<</dialog>>
<<dialog "dinerWaitress1">>
But then you look up and five years are gone. Ten years are gone. And someone like Diana walks through that door and everyone looks up.
<</dialog>>
<<dialog "dinerWaitress1">>
Nobody tells you this. But I'm telling you: be consistent. Every day. Your skin, your hair, how you carry yourself small things, but they add up.
<</dialog>>
<<dialog "dinerWaitress1">>
Ten years from now you've either built something or you're watching the woman who just walked through that door.
<</dialog>>
<<dialog "player">>
I never thought about it like that, honestly.
<</dialog>>
<<dialog "dinerWaitress1">>
(looking at you)
It shows.
<</dialog>>
<<dialog "dinerWaitress1">>
Do you use moisturizer?
<</dialog>>
<<dialog "player">>
Not really.
<</dialog>>
<<dialog "dinerWaitress1">>
Every night. Neck too.
<</dialog>>
<<narrative>>
She turned around and went back to work.
<</narrative>>
<<narrative>>
Emma's "back then" stuck with you long after she went back to work.
<</narrative>>
<<dialog "player">>
(Inner voice)
Was she jealous of Diana or of Vince? I couldn't tell.
"There was this one time... forget it."
What happened that time?
Maybe Sofia knows.
<</dialog>>
<<nobr>>
<<set $flags.emmaGossipDone = true>>
<<set $flags.sofiaGossipUnlocked = true>>
<<completeObjective "something_different" "talkEmma">>
<</nobr>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerWaitress1.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div><<nobr>>
<<silently>>
<<if ($health || 0) <= 15 and !$hospitalAdmissionInProgress and !$_navigatingBackward>>
<<goto "hospital_admission">>
<</if>>
/* passagestart clears $location restore scene id for body[data-location] / backgrounds */
<<if ndef $collapseAtLocation>><<set $collapseAtLocation = $collapseReturnTo || "fhBedroom">><</if>>
<<if ndef $collapseReturnTo>><<set $collapseReturnTo = $collapseAtLocation || "fhBedroom">><</if>>
<<set $location = $collapseAtLocation>>
/* Emergency normalization to prevent immediate needs-loop while collapsed. */
<<set $hunger = 35>>
<<set $thirst = 35>>
<<set $bladder = 35>>
<<set _collapseVariant = random(1, 3)>>
/* Check inventory for any item that restores energy */
<<set _hasEnergyItem = false>>
<<if $inventory and $inventory.length > 0>>
<<for _invEntry range $inventory>>
<<if _invEntry and _invEntry.id>>
/* Search all item categories */
<<set _itemData = null>>
<<for _cat range Object.keys(setup.items)>>
<<if !_itemData>>
<<set _found = setup.items[_cat].find(function(i) { return i.id === _invEntry.id; })>>
<<if _found>><<set _itemData = _found>><</if>>
<</if>>
<</for>>
<<if _itemData and _itemData.effects>>
<<set _energyEffect = _itemData.effects.find(function(e) { return e.stat === "energy" && e.value > 0; })>>
<<if _energyEffect>><<set _hasEnergyItem = true>><</if>>
<</if>>
<</if>>
<</for>>
<</if>>
<</silently>>
<</nobr>>
<<if _collapseVariant === 1>>
<<narrative "Exhausted">>
Your vision blurs at the edges. Your legs feel like lead, each step more uncertain than the last.
<</narrative>>
<<dialog "player">>
(Inner voice)
I can't keep going like this...
<</dialog>>
<<narrative>>
The world tilts slightly. You reach for the nearest wall, steadying yourself. Your heart is pounding, though you haven't done anything to earn it.
<</narrative>>
<<narrative>>
You need to do something now.
<</narrative>>
<<elseif _collapseVariant === 2>>
<<narrative "Running on Fumes">>
Your body starts sending signals all at once: trembling hands, weak knees, a headache that pulses behind your eyes.
<</narrative>>
<<dialog "player">>
(Inner voice)
Not here. Not now.
<</dialog>>
<<narrative>>
You lean against a wall and wait for the spinning to stop, but it only gets worse.
<</narrative>>
<<narrative>>
If you don't act immediately, you're going down.
<</narrative>>
<<else>>
<<narrative "At the Limit">>
A wave of dizziness hits so hard you freeze mid-step. Breathing feels shallow, and your legs barely respond.
<</narrative>>
<<dialog "player">>
(Inner voice)
Come on. Stay up.
<</dialog>>
<<narrative>>
You force yourself to focus on one thing at a time, fighting to keep your balance.
<</narrative>>
<<narrative>>
You need a quick fix or a way home. Immediately.
<</narrative>>
<</if>>
<div class="location-actions">
<<if _hasEnergyItem>>
<<btn "Use an Item" "energy_collapse_items">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Nothing in your bag to help right now">
<span class="icon icon-lock icon-12"></span> Use an Item
</span>
<</if>>
<<btn "Go Home" "energy_collapse_nap">><</btn>>
</div><<nobr>>
<<silently>>
<<set $location = $collapseAtLocation || $collapseReturnTo>>
/* Build list of energy-restoring items from inventory */
<<set _energyItems = []>>
<<if $inventory and $inventory.length > 0>>
<<for _invEntry range $inventory>>
<<if _invEntry and _invEntry.id>>
<<set _itemData = null>>
<<for _cat range Object.keys(setup.items)>>
<<if !_itemData>>
<<set _found = setup.items[_cat].find(function(i) { return i.id === _invEntry.id; })>>
<<if _found>><<set _itemData = _found>><</if>>
<</if>>
<</for>>
<<if _itemData and _itemData.effects>>
<<set _energyEffect = _itemData.effects.find(function(e) { return e.stat === "energy" && e.value > 0; })>>
<<if _energyEffect>>
<<run _energyItems.push({ id: _invEntry.id, name: _itemData.name, qty: _invEntry.quantity || 1 })>>
<</if>>
<</if>>
<</if>>
<</for>>
<</if>>
<</silently>>
<</nobr>>
<<narrative "What's in Your Bag">>
You dig through your bag with unsteady hands. Your fingers are slow, clumsy. Every second matters.
<</narrative>>
<<dialog "player">>
(Inner voice)
Come on, there has to be something...
<</dialog>>
<<if _energyItems.length > 0>>
<div class="location-actions">
<<for _item range _energyItems>>
<<capture _item>>
<<btn _item.name "energy_collapse_use">>
<<set $collapseUseItemId = _item.id>>
<</btn>>
<</capture>>
<</for>>
<<btn "Never mind" "energy_collapse">><</btn>>
</div>
<<else>>
<<narrative>>
There's nothing useful. Your bag is empty of anything that could help.
<</narrative>>
<<dialog "player">>
(Inner voice)
Of course...
<</dialog>>
<div class="location-actions">
<<btn "Go Home" "energy_collapse_nap">><</btn>>
</div>
<</if>><<silently>>
<<set $location = "fhBedroom">>
<<set $isSleeping = true>>
<<advanceTime 90>>
<<set $isSleeping = false>>
<<gainStat "energy" 20>>
<<loseStat "health" 10>>
<<gainStat "stress" 15>>
<<set $appearance.hairCombed = 0>>
<<set _criticalAfterCrash = (($health || 0) <= 15)>>
<<set _napVariant = random(1, 3)>>
<<set _returnTo = $collapseReturnTo || "fhBedroom">>
<<set $collapseReturnTo = null>>
<<set $collapseAtLocation = null>>
/* Prevent immediate re-collapse loops after emergency rest. */
<<set $hunger = 35>>
<<set $thirst = 35>>
<<set $bladder = 35>>
<<if !_criticalAfterCrash>>
<<recalculateStats>>
<</if>>
<</silently>>
<<if _criticalAfterCrash and !$_navigatingBackward>>
<<if _napVariant === 1>>
<<narrative "Barely Made It Home">>
You make it to your room on instinct more than strength. The moment you collapse onto the bed, the pain in your chest sharpens and your breathing turns shallow.
<</narrative>>
<<dialog "player">>
(Inner voice)
Something is really wrong...
<</dialog>>
<<narrative>>
The room blurs again. Distant voices, hurried footsteps, then sirens somewhere far too close.
<</narrative>>
<<elseif _napVariant === 2>>
<<narrative "Too Late">>
You stumble through the door and barely reach the bed before your body locks up with a sharp, spreading ache.
<</narrative>>
<<dialog "player">>
(Inner voice)
No... this isn't just exhaustion.
<</dialog>>
<<narrative>>
Your breathing turns ragged. The ceiling swims. Somewhere outside your room, panic rises into urgent voices.
<</narrative>>
<<else>>
<<narrative "On Empty">>
You collapse face-first onto the mattress, expecting sleep to pull you under.
Instead, a crushing tightness settles in your chest and refuses to ease.
<</narrative>>
<<dialog "player">>
(Inner voice)
I need help. Now.
<</dialog>>
<<narrative>>
You try to call out, but the sound comes out thin. The next thing you register is a distant siren cutting through the dark.
<</narrative>>
<</if>>
<<goto "hospital_admission">>
<</if>>
<<if _napVariant === 1>>
<<narrative "Barely Made It Home">>
You don't remember much of the walk back. One foot in front of the other, keeping your eyes on the pavement, willing your body to keep moving.
The front door. The stairs. Your room.
You don't even change. You drop onto the bed fully dressed, and the moment your head hits the pillow, everything goes dark.
<</narrative>>
<<narrative "90 Minutes Later">>
You come back slowly. The ceiling. The familiar smell of your room. Your mouth is dry and your body aches, but you're conscious.
<</narrative>>
<<dialog "player">>
(Inner voice)
That was stupid. I pushed too far.
<</dialog>>
<<narrative>>
You sit up carefully. The world doesn't tilt this time. A small improvement.
<</narrative>>
<<dialog "player">>
(Inner voice)
I need to be more careful.
<</dialog>>
<<elseif _napVariant === 2>>
<<narrative "Crash Landing">>
By the time you reach your room, your hands are shaking hard enough that you fumble the doorknob twice.
You finally make it inside, fall across the bed, and vanish into a heavy, dreamless sleep.
<</narrative>>
<<narrative "After the Nap">>
You wake with a dry throat and a stiff neck, still tired but no longer on the edge of collapse.
<</narrative>>
<<dialog "player">>
(Inner voice)
I keep gambling with my limits.
<</dialog>>
<<narrative>>
You sit still for a minute, waiting for your heartbeat to settle before standing.
<</narrative>>
<<dialog "player">>
(Inner voice)
Next time I stop before it gets this bad.
<</dialog>>
<<else>>
<<narrative "Last Reserve">>
You drag yourself upstairs by memory alone. The bed is there, and that is enough.
The second you lie down, exhaustion takes over like a switch flipping off.
<</narrative>>
<<narrative "A Little Later">>
When you wake, your body still aches, but the dizziness has mostly cleared. Your breathing is steady again.
<</narrative>>
<<dialog "player">>
(Inner voice)
I almost paid for that one.
<</dialog>>
<<narrative>>
You rub your face, then swing your legs off the bed with careful, deliberate movements.
<</narrative>>
<<dialog "player">>
(Inner voice)
No more pretending I'm fine when I'm not.
<</dialog>>
<</if>>
<div class="location-actions">
<<btn "Get Up" "fhBedroom">><</btn>>
</div><<silently>>
<<set $location = $collapseAtLocation || $collapseReturnTo>>
/* Apply the selected item's energy effect */
<<set _usedItem = null>>
<<set _energyGain = 0>>
<<for _cat range Object.keys(setup.items)>>
<<if !_usedItem>>
<<set _found = setup.items[_cat].find(function(i) { return i.id === $collapseUseItemId; })>>
<<if _found>><<set _usedItem = _found>><</if>>
<</if>>
<</for>>
<<if _usedItem and _usedItem.effects>>
<<for _effect range _usedItem.effects>>
<<if _effect.stat === "energy" and _effect.value > 0>>
<<set _energyGain = _effect.value>>
<<gainStat "energy" _energyGain>>
<</if>>
/* Apply other effects too (mood, focus, hunger etc.) */
<<if _effect.stat !== "energy" and _effect.type === "instant">>
<<if _effect.value > 0>>
<<gainStat _effect.stat _effect.value>>
<<else>>
<<loseStat _effect.stat (0 - _effect.value)>>
<</if>>
<</if>>
<</for>>
/* Remove one use from inventory */
<<set _invIdx = $inventory.findIndex(function(i) { return i.id === $collapseUseItemId; })>>
<<if _invIdx >= 0>>
<<if $inventory[_invIdx].quantity > 1>>
<<set $inventory[_invIdx].quantity -= 1>>
<<else>>
<<run $inventory.splice(_invIdx, 1)>>
<</if>>
<</if>>
<</if>>
<<set _returnTo = $collapseReturnTo || "fhBedroom">>
<<set $collapseReturnTo = null>>
<<set $collapseAtLocation = null>>
<<set $collapseUseItemId = null>>
/* Prevent immediate re-collapse loops after emergency item use. */
<<set $hunger = 35>>
<<set $thirst = 35>>
<<set $bladder = 35>>
<<recalculateStats>>
<</silently>>
<<if _usedItem>>
<<narrative "That's Better">>
You pull out the <<print _usedItem.name>> and consume it right there. The effect isn't instant, but it's enough the dizziness starts to lift, the world stops tilting.
<</narrative>>
<<dialog "player">>
(Inner voice)
Okay. Okay. I'm still standing.
<</dialog>>
<<narrative>>
You take a breath. Slow and deliberate. Your legs are still heavy, but you can move.
<</narrative>>
<<else>>
<<narrative "That's Better">>
The item doesn't seem to help as much as you hoped. But at least you're still on your feet.
<</narrative>>
<</if>>
<div class="location-actions">
<<btn "Keep Going" _returnTo>><</btn>>
</div><<nobr>>
<<set $location = "fhLivingroom">>
<<updateCharacterLocations>>
<<narrative "Living Room">>
<<image "assets/content/scenes/maplewood/firstJobDay/sayHello.webp" "100%">>
<</narrative>>
<<narrative>>
When you step into the living room, you notice your mom and dad on the couch, watching TV. The moment they see you, the remote goes downthe screen goes darkand their attention snaps to you.
<</narrative>>
<<dialog "player">>
Hey, everyone.
<</dialog>>
<<narrative>>
Your mom sits up a little.
<</narrative>>
<<dialog "mother">>
Welcome home, honey…
<</dialog>>
<<narrative>>
A few seconds of quiet.
<</narrative>>
<<dialog "father">>
Welcome home. How was your day?
<</dialog>>
<<narrative>>
The question should be easy. It isn't. You hesitate, take a shallow breath.
<</narrative>>
<div class="location-actions">
<<btn "It went great." "event_fhLivingroom_firstWorkDayFamily_great">><</btn>>
<<btn "It was okay…" "event_fhLivingroom_firstWorkDayFamily_okay" "secondary">><</btn>>
</div>
<</nobr>><div class="divnobr">
<<set $location = "fhLivingroom">>
<<updateCharacterLocations>>
<<gainStat "willpower" 2>>
<<gainStat "mood" 5>>
<<flushNotifications>>
<<set $flags.firstWorkDayWalkHome = false>>
<<narrative "Living Room">>
You find your voice. It comes out calmer than you expected.
<</narrative>>
<<dialog "player">>
It was a lot… but I did it. First day is over.
<</dialog>>
<<dialog "player">>
People were nice. It was obvious I was the new one, but nobody made a big deal out of it.
<</dialog>>
<<narrative>>
Your mom listens carefully, a small smile forming on her face.
<</narrative>>
<<dialog "mother">>
That's what I wanted to hear. Not that it was easy but that you got through it.
<</dialog>>
<<dialog "father">>
Good. That's how it starts.
<</dialog>>
<<narrative>>
Something small and warm settles in your chest.
<</narrative>>
<div class="location-actions">
<<btn "Continue" "fhLivingroom">><</btn>>
</div><div class="divnobr">
<<set $location = "fhLivingroom">>
<<updateCharacterLocations>>
<<loseStat "mood" 5>>
<<flushNotifications>>
<<set $flags.firstWorkDayWalkHome = false>>
<<narrative "Living Room">>
You almost shrug, but catch yourself.
<</narrative>>
<<dialog "player">>
It was okay… I guess.
<</dialog>>
<<dialog "player">>
My head felt heavy the whole time. Everyone already knew each other. I was still the new girl.
<</dialog>>
<<narrative>>
Your dad nods slowly, like he understands exactly what you mean.
<</narrative>>
<<dialog "father">>
First days are always like that. Doesn't mean you failed.
<</dialog>>
<<dialog "mother">>
You still came home and told us how it really felt. That counts for something.
<</dialog>>
<<narrative>>
It doesn't fix everything, but it still sits nicely inside you.
<</narrative>>
<div class="location-actions">
<<btn "Continue" "fhLivingroom">><</btn>>
</div>
<</div>><<nobr>>
<<set $location = "maplewood">>
<<updateCharacterLocations>>
<<image "assets/content/scenes/maplewood/firstJobDay/mappleNight.webp" "100%">>
<<narrative "Maplewood">>
It hadn't even been a few days since you left home, but time stretched here in a strange way.
<</narrative>>
<<narrative>>
Since you got here, you'd never felt this brave.
<</narrative>>
<<dialog "player">>
(Inner voice)
Brave? You?
<</dialog>>
<<narrative>>
You'd always dodged admitting you kept to yourself.
<</narrative>>
<<narrative>>
But now you could confess it quietly, to yourself: for a long time you'd chosen to stay where it felt safe.
<</narrative>>
<<narrative>>
You'd never had a job. You'd wanted one, really… but you'd been scared.
<</narrative>>
<<dialog "player">>
(Inner voice)
Was it embarrassing yourself? Giving up? Or someone saying you weren't enough? You couldn't name it exactly.
<</dialog>>
<<narrative>>
When school ended it felt like life's door cracked open. Getting to the threshold was easy; stepping through was something else. Today you stepped.
<</narrative>>
<<narrative>>
When your first shift ended there was a strange feeling inside: this wasn't "just a job." For the first time you'd touched real life from the outside, under your own name.
<</narrative>>
<<narrative>>
You'd thought the big city would swallow you. You'd imagined streets turned against you. That fear wasn't entirely wrongbut here you were in Maplewood now.
<</narrative>>
<<image "assets/content/scenes/maplewood/firstJobDay/mapplePorch.webp" "100%">>
<<narrative>>
Porch lights glowed. TV noise drifted from somewhere. Cooking smell hung in the air. It didn't feel that foreign.
<</narrative>>
<<dialog "player">>
(Inner voice)
You'd been talking like everything changed. When really you'd just grown a little.
<</dialog>>
<<narrative>>
The thought spread quietly in your chest. Not shame. Quiet, warm pride.
<</narrative>>
<<dialog "player">>
(Inner voice)
You did it.
<</dialog>>
<<dialog "player">>
(Inner voice)
You went.
<</dialog>>
<<dialog "player">>
(Inner voice)
You held on.
<</dialog>>
<<narrative>>
You were tired, but the tiredness felt good.
<</narrative>>
<<narrative>>
The diner noise still rang in your ears; your feet achedbut none of it crushed you. If anything it made you feel more real.
<</narrative>>
<<narrative>>
You let your shoulders sink slowly. The noise stayed behind. In its place: crickets, the soft scuff of your shoes.
<</narrative>>
<<narrative>>
You'd been so far inside your own head you almost missed itthen the curb, the front steps, your own porch light swam into focus.
<</narrative>>
<<narrative>>
Your legs had carried you home while you weren't paying attention.
<</narrative>>
<div class="location-actions">
<<btn "Enter home" "fhLivingroom">>
<<silently>>
<<set $flags.firstWorkDayWalkEvent = false>>
<<set $flags.firstWorkDayWalkHome = true>>
<</silently>>
<</btn>>
</div>
<</nobr>>
<<set _destination = $tempTaxiDestination>>
<<set _cost = $tempTaxiCost>>
<<if $money >= _cost>>
<<set $money -= _cost>>
<div class="taxi-screen">
<h2>🚕 Taxi Called</h2>
<p>Fare: $<<= _cost>></p>
<p>Remaining: $<<= $money>></p>
<<link "Arrive at Destination" _destination>><</link>>
</div>
<<else>>
<div class="taxi-screen">
<h2>❌ Not Enough Money</h2>
<p>Need: $<<= _cost>></p>
<p>Have: $<<= $money>></p>
<<link "Go Back" previous()>><</link>>
</div>
<</if>><<silently>>
<<set $location = "fhBackyard">>
<<logDailyActivity "father" "talk">>
<<set _stats = $characters.father.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Phase */
<<set _phase = (def $flags && $flags.fatherStartedWork) ? "postWork" : "preWork">>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/*
Backyard presence by phase:
preWork → morning + afternoon (father home all day)
postWork → father rarely in backyard on weekdays (schedule: Kitchen 18:00, Garage 19:30, LivingRoom 20:00)
Weekend only → afternoon
Fallback: evening → afternoon, morning (preWork only)
*/
<<set _contextKey = "fhBackyard_" + _phase + "_" + _timeSlot>>
<<set _phasePool = setup.fatherTopics[_phase] && setup.fatherTopics[_phase]["level" + _level]>>
<<set _context = (_phasePool && _phasePool[_contextKey]) ? _phasePool[_contextKey] : null>>
/* Fallback */
<<if !_context && _phasePool>>
/* evening → afternoon */
<<set _context = _phasePool["fhBackyard_" + _phase + "_afternoon"]>>
<<if !_context>>
/* preWork has morning too */
<<set _context = _phasePool["fhBackyard_preWork_afternoon"]>>
<</if>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time and stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
/* Apply stat gains */
<<if _topic.friendship>><<gainCharacterStat "father" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "father" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "father" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<narrative "Backyard - Talk with Father">>
<<if _phase === "preWork">>
Your father's out in the garden assessing, working, or simply standing in it with a mug of tea.
<<else>>
He's rare in the backyard since work started, but here he is making use of the daylight.
<</if>>
<</narrative>>
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.father.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhGarage">>
<<logDailyActivity "father" "talk">>
<<set _stats = $characters.father.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Phase */
<<set _phase = (def $flags && $flags.fatherStartedWork) ? "postWork" : "preWork">>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/*
Garage by phase:
preWork → morning + afternoon
postWork → evening only (19:30 slot in schedule)
Fallback: postWork morning/afternoon → evening
preWork evening → afternoon → morning
*/
<<set _contextKey = "fhGarage_" + _phase + "_" + _timeSlot>>
<<set _phasePool = setup.fatherTopics[_phase] && setup.fatherTopics[_phase]["level" + _level]>>
<<set _context = (_phasePool && _phasePool[_contextKey]) ? _phasePool[_contextKey] : null>>
<<if !_context && _phasePool>>
<<if _phase === "postWork">>
/* postWork garage = evening only */
<<set _context = _phasePool["fhGarage_postWork_evening"]>>
<<else>>
/* preWork: evening → afternoon → morning */
<<set _context = _phasePool["fhGarage_preWork_afternoon"]>>
<<if !_context>>
<<set _context = _phasePool["fhGarage_preWork_morning"]>>
<</if>>
<</if>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time and stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
/* Apply stat gains */
<<if _topic.friendship>><<gainCharacterStat "father" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "father" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "father" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<narrative "Garage - Talk with Father">>
<<if _phase === "preWork">>
The garage smells of engine oil and fresh wood. Your father's in his element this space is his.
<<else>>
He's been in the garage for a while. The work day gets left at the door here.
<</if>>
<</narrative>>
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.father.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhKitchen">>
<<logDailyActivity "father" "talk">>
<<set _stats = $characters.father.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Phase */
<<set _phase = (def $flags && $flags.fatherStartedWork) ? "postWork" : "preWork">>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Context key */
<<set _contextKey = "fhKitchen_" + _phase + "_" + _timeSlot>>
<<set _phasePool = setup.fatherTopics[_phase] && setup.fatherTopics[_phase]["level" + _level]>>
<<set _context = (_phasePool && _phasePool[_contextKey]) ? _phasePool[_contextKey] : null>>
/* Fallback: postWork only has morning+evening afternoon → evening */
<<if !_context && _phasePool>>
<<if _timeSlot === "afternoon">>
<<set _context = _phasePool["fhKitchen_" + _phase + "_evening"]>>
<</if>>
/* preWork has morning+evening if still null try morning */
<<if !_context>>
<<set _context = _phasePool["fhKitchen_" + _phase + "_morning"]>>
<</if>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time and stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
/* Apply stat gains */
<<if _topic.friendship>><<gainCharacterStat "father" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "father" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "father" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<narrative "Kitchen - Talk with Father">>
<<if _phase === "preWork">>
The kitchen has that early-morning feel unhurried, still settling in. Your father's here too, fitting the new house into his routines.
<<elseif _timeSlot === "morning">>
He's moving with purpose commute mode. The kitchen is a brief stop before his day starts.
<<else>>
He's just come in from work. The kitchen is where he lands first jacket off, winding down.
<</if>>
<</narrative>>
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.father.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhLivingroom">>
<<logDailyActivity "father" "talk">>
<<set _stats = $characters.father.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Phase */
<<set _phase = (def $flags && $flags.fatherStartedWork) ? "postWork" : "preWork">>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Context key */
<<set _contextKey = "fhLivingroom_" + _phase + "_" + _timeSlot>>
<<set _phasePool = setup.fatherTopics[_phase] && setup.fatherTopics[_phase]["level" + _level]>>
<<set _context = (_phasePool && _phasePool[_contextKey]) ? _phasePool[_contextKey] : null>>
/* Fallback:
postWork LivingRoom only has evening
preWork LivingRoom has morning/afternoon/evening
*/
<<if !_context && _phasePool>>
<<set _context = _phasePool["fhLivingroom_" + _phase + "_evening"]>>
<<if !_context>>
<<set _context = _phasePool["fhLivingroom_" + _phase + "_morning"]>>
<</if>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time and stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
/* Apply stat gains */
<<if _topic.friendship>><<gainCharacterStat "father" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "father" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "father" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<narrative "Living Room - Talk with Father">>
<<if _phase === "preWork">>
The living room is still finding its shape furniture half-settled, the new house making itself familiar. Your father's here, part of it.
<<else>>
He's in the living room after work the decompression ritual in progress.
<</if>>
<</narrative>>
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.father.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhParentsRoom">>
<<logDailyActivity "father" "talk">>
<<set _stats = $characters.father.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Phase */
<<set _phase = (def $flags && $flags.fatherStartedWork) ? "postWork" : "preWork">>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Context key ParentsRoom */
<<set _contextKey = "fhParentsRoom_" + _phase + "_" + _timeSlot>>
<<set _phasePool = setup.fatherTopics[_phase] && setup.fatherTopics[_phase]["level" + _level]>>
<<set _context = (_phasePool && _phasePool[_contextKey]) ? _phasePool[_contextKey] : null>>
/* Fallback:
ParentsRoom preWork has morning + evening only
ParentsRoom postWork has evening only
afternoon → morning (preWork) or evening (postWork)
*/
<<if !_context && _phasePool>>
<<if _phase === "preWork">>
<<if _timeSlot === "afternoon">>
<<set _context = _phasePool["fhParentsRoom_preWork_morning"]>>
<</if>>
<<if !_context>>
<<set _context = _phasePool["fhParentsRoom_preWork_evening"]>>
<</if>>
<<else>>
<<set _context = _phasePool["fhParentsRoom_postWork_evening"]>>
<</if>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time and stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
/* Apply stat gains */
<<if _topic.friendship>><<gainCharacterStat "father" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "father" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "father" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.father.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<nobr>>
<<set $location = "fhBackyard">>
<<set $activityOrigin = "fhBackyard">>
<<updateCharacterLocations>>
<<narrative "Backyard">>
A peaceful garden with a well-maintained lawn. A small patio offers shade, and flower beds add color to the scene.
<</narrative>>
<<showLocationChars "fhBackyard">>
<div class="location-actions">
<<btnPicker "Rest" "backyardRest" "mediumActivity">>
<<yogaActions>>
</div>
<<navMenu>>
<<navCard "fhDownstairs">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "fhBedroom">>
<<updateCharacterLocations>>
<<narrative "My Bed">>
You approach your bed. It looks inviting and comfortable, perfect for sleeping or taking a quick nap.
<</narrative>>
<<showLocationChars "fhBedroom">>
<<bedActions>>
<div class="location-actions">
<<btn "Back" "fhBedroom">><</btn>>
</div>
<</nobr>><<nobr>>
<<script>>
if (window.QuestSceneNav && typeof window.QuestSceneNav.scheduleHubAutoScene === "function") {
window.QuestSceneNav.scheduleHubAutoScene("fhBedroom");
}
<</script>>
<<set $location = "fhBedroom">>
<<updateCharacterLocations>>
<<narrative "My Bedroom">>
Your room. Posters on the walls, your things where you left them, small, but yours.
<</narrative>>
<<showLocationChars "fhBedroom">>
<<silently>>
<<set _hasLaptop = false>>
<<if $inventory && $inventory.length > 0>>
<<for _invItem range $inventory>>
<<if _invItem.id === "laptop" && (_invItem.quantity || 0) > 0>>
<<set _hasLaptop = true>>
<<break>>
<</if>>
<</for>>
<</if>>
<</silently>>
<div class="location-actions">
<<if _hasLaptop>>
<<btn "Use Laptop" "laptopComputerMenu">><</btn>>
<</if>>
<<readBtn>>
<<btn "Go to Bed" "fhBed">><</btn>>
</div>
<<navMenu>>
<<navCard "fhWardrobe">>
<<navCard "fhUpperstairs">>
<</navMenu>>
<</nobr>>
/* ==========================================
EVENT - Mirror Moment
Triggers from fhBedroom after emmaGossipDone.
Leads player to brother's PC.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhBedroom">>
<<advanceTime 20>>
<<advanceQuestStage "something_different">>
<</silently>>
<<narrative "Bedroom">>
You closed the door behind you.
You didn't sit down. You walked straight to the window and leaned against the glass.
<</narrative>>
<<vid "assets/content/scenes/events/diana/windowLook.mp4" "100%">>
<<narrative>>
The street below was the same as yesterday. Same houses. Same trees.
<</narrative>>
<<narrative>>
There was someone you liked in high school. Quietly. From a distance. You never said a word.
You waited for him to notice. Days. Weeks. Like one morning he'd just look up and see you.
He didn't.
<</narrative>>
<<narrative>>
Then a new girl walked into class. The room changed the day she arrived you felt it. Everyone did.
You watched. They talked. Then they were always together.
You were still in the same seat. Same corner.
<</narrative>>
<<narrative>>
She wasn't prettier. Wasn't smarter. But when she walked in, you felt her there. Like she knew how to be seen.
You'd always waited. For someone else to make the first move.
Nobody had.
<</narrative>>
<<dialog "player">>
(Inner voice)
What did she have that I didn't?
<</dialog>>
<<narrative>>
You picked up the hand mirror from the desk.
You looked.
<</narrative>>
<<vid "assets/content/scenes/events/diana/checkMirror.mp4" "100%">>
<<narrative>>
Vince's voice came back, clear as if he were standing in the room.
"Do you ever look at yourself in the mirror?"
<</narrative>>
<<dialog "player">>
(Inner voice)
I thought I'd made peace with it. I thought I'd let it go.
Diana walked inand I knew I hadn't.
<</dialog>>
<<narrative>>
You'd frozen that day. Your face had burned. You hadn't said a word.
<</narrative>>
<<narrative>>
Now you looked. Really looked.
Dry skin. Shadows under your eyes. Your hair just... there. Had it always looked like that.
<</narrative>>
<<dialog "player">>
(Inner voice)
One look at me back there dishwasher, kitchen and he already knew.
"No one has to see you there."
He didn't even need to say it twice.
<</dialog>>
<<narrative>>
Then Diana came to mind like someone stepping into light that was already waiting for her.
You'd never walked into a room like that. Not once.
<</narrative>>
<<narrative>>
Vince's voice again, flat: "It shows."
<</narrative>>
<<narrative>>
You held the mirror a long time.
<</narrative>>
<<dialog "player">>
(Inner voice)
Is this my life now for good?
<</dialog>>
<<narrative>>
You set the mirror down.
Your brother's room came to mind his desk. His computer.
<</narrative>>
<div class="location-actions">
<<btn "Go to your brother's room" "brotherComputer_beautySearch">><</btn>>
</div>
</div>/* ==========================================
EVENT - Mall Aftermath (Something Different arc)
Bedroom breakdown after the mall humiliation.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhBedroom">>
<<advanceTime 20>>
<<advanceQuestStage "something_different">>
<</silently>>
<<narrative "Bedroom">>
The second you close the door, the silence drops over you.
<</narrative>>
<<narrative>>
You sit on the edge of the bed. Your knees go heavy. Your shoulders fold in. The weight of the whole day seems to land on you all at once.
<</narrative>>
<<narrative>>
The woman from the mall comes back first.
<</narrative>>
<<narrative>>
That smile.
<</narrative>>
<<narrative>>
That calm, condescending look in her eyes. The kind that said everything without needing to say much at all.
<</narrative>>
<<narrative>>
Your eyes fill.
<</narrative>>
<<vid "assets/content/scenes/events/diana/mallAftermathEyesFill.mp4" "100%">>
<<narrative>>
This time you can't stop it. You cover your face with your hands, but tears slip through your fingers anyway.
<</narrative>>
<<narrative>>
She didn't just tell you what you couldn't afford.
<</narrative>>
<<narrative>>
She told you where you belonged.
<</narrative>>
<<narrative>>
Then you think of Diana.
<</narrative>>
<<narrative>>
The moment she walked into Ruby's, the whole restaurant turned toward her. Vince. The kitchen guys. Even the girls.
<</narrative>>
<<narrative>>
It was like the light found her on its own.
<</narrative>>
<<narrative>>
When you walk in, people talk prices.
<</narrative>>
<<narrative>>
They tell you where to go instead.
<</narrative>>
<<dialog "player">>
(Inner voice)
It comes back againthat same corner at school, that same invisible feeling.
He didn't see me then. Why would anyone see me now?
<</dialog>>
<<narrative>>
You stay there on the edge of the bed, crying quietly.
<</narrative>>
<<narrative>>
You don't want to be that invisible girl anymore.
<</narrative>>
<<narrative>>
The girl who gets placed in her spot with one look. The girl who gets told, without anyone needing to say it, that she doesn't belong.
<</narrative>>
<<dialog "player">>
(Inner voice)
I won't be that girl again.
Whatever it takes.
<</dialog>>
<<narrative>>
You wipe your eyes and take a slow breath.
<</narrative>>
<<narrative>>
This wasn't about taking care of yourself anymore.
<</narrative>>
<<narrative>>
It was about not shrinking the second you walked into a room.
<</narrative>>
<<narrative>>
It was about being able to stand there and actually take up space.
<</narrative>>
<<dialog "player">>
(Inner voice)
If money is what it takes, I'll find it.
<</dialog>>
<div class="location-actions">
<<btn "Continue" "fhBedroom">><</btn>>
</div>
</div><<nobr>>
<<set $location = "fhBrotherRoom">>
<<updateCharacterLocations>>
<<narrative "Brother's Room">>
Your brother's room is messier than yours. Gaming posters cover the walls and clothes are scattered on the floor.
<</narrative>>
<<showLocationChars "fhBrotherRoom">>
<<questPrompts>>
<div class="location-actions">
<<useComputerWidget>>
<<if $characters.brother && $characters.brother.currentLocation === "fhBrotherRoom">>
<<btn "Watch Brother" "brotherWatch">><</btn>>
<</if>>
</div>
<<navMenu>>
<<navCard "fhUpperstairs">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "fhLivingroom">>
<<updateCharacterLocations>>
<<narrative "Living Room Couch">>
You sit down on the soft, plush couch. It's perfectly positioned in front of the TV, and the cushions are deep enough to sink into. Whether you want to catch a show or just close your eyes for a bit, this is the spot.
<</narrative>>
<<showLocationChars "fhLivingroom">>
<<couchActions>>
<div class="location-actions">
<<btn "Go Back" "fhLivingroom">><</btn>>
</div>
<</nobr>><<nobr>>
<<set $location = "fhDownbath">>
<<set $returnAfterToilet = "fhDownbath">>
<<updateCharacterLocations>>
<<narrative "Downstairs Bathroom">>
A small guest bathroom near the main entrance. Clean and functional, with fresh towels neatly folded.
<</narrative>>
<<showLocationChars "fhDownbath">>
<div class="location-actions">
<<bathroomActions>>
<<mirrorBtn>>
</div>
<<navMenu>>
<<navCard "fhDownstairs">>
<</navMenu>>
<</nobr>><<nobr>>
<<set State.temporary.fhDownstairsPlayPassage = "">>
<<if $timeSys.hour === 0 && $daily.fhThinWallsNight !== $timeSys.day>>
<<set $daily.fhThinWallsNight = $timeSys.day>>
<<if random(1, 100) <= 20 && !State.variables._navigatingBackward>>
<<set State.temporary.fhDownstairsPlayPassage = "fhDownstairs_event_parentsThinWalls">>
<</if>>
<</if>>
<<if State.temporary.fhDownstairsPlayPassage !== "">>
<<script>>
setTimeout(function () {
var p = State.temporary.fhDownstairsPlayPassage;
State.temporary.fhDownstairsPlayPassage = "";
if (!p || State.variables._navigatingBackward) return;
if (State.passage !== "fhDownstairs") return;
var Eng = typeof Engine !== "undefined" ? Engine : (typeof SugarCube !== "undefined" ? SugarCube.Engine : null);
if (Eng && typeof Eng.play === "function") Eng.play(p);
}, 0);
<</script>>
<</if>>
<<set $location = "fhDownstairs">>
<<updateCharacterLocations>>
<<narrative "Downstairs">>
The main floor of the house connects all the common areas. The stairs lead up to the bedrooms, while doors open to the living spaces.
<</narrative>>
<<showLocationChars "fhDownstairs">>
<<navMenu>>
<<navCard "fhKitchen">>
<<navCard "fhLivingroom">>
<<navCard "fhDownbath">>
<<navCard "fhParentsRoom">>
<<navCard "fhBackyard">>
<<navCard "fhGarage">>
<<navCard "fhUpperstairs">>
<<navCard "maplewood">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "fhDownstairs">>
<<set _thinVar = random(1, 3)>>
<<narrative "Downstairs">>
You had stopped on the main floor with one foot still on the bottom stair, half-thinking about water or bed. The house held that thick midnight quietuntil a thin sound threaded through it from the direction of your parents' room.
<</narrative>>
<<narrative "Downstairs">>
You tell yourself not to. *Just go upstairs.* Your body doesn't listen. A few careful steps carry you down the hall; you lean in until your ear is almost flush with the wall, breath held.
<</narrative>>
<<if _thinVar === 1>>
<<narrative "Downstairs">>
On the other side of plaster and paint, the rhythm finds youragged inhales, a soft cry cut short, the headboard's faint tap. Another moan, higher, then a muffled laugh and a shush. They think the house is asleep.
<</narrative>>
<<dialog "player">>
(Inner voice)
Oh my God. I should *not* be here.
<</dialog>>
<<elseif _thinVar === 2>>
<<narrative "Downstairs">>
The voices are barely wordsyour ear is so close that meaning leaks through anyway.
<</narrative>>
<<dialog "father">>
(muffled)
Where'd we put the condom
<</dialog>>
<<dialog "mother">>
(whisper)
Nightstand*shh*
<</dialog>>
<<narrative "Downstairs">>
Rustling, a stifled giggle, then silence that still isn't quiet.
<</narrative>>
<<else>>
<<narrative "Downstairs">>
The noises thin to nothing. Then, intimate as a secret through the wall:
<</narrative>>
<<dialog "mother">>
(murmur)
That was so good.
<</dialog>>
<<dialog "father">>
(low)
Come here.
<</dialog>>
<<narrative "Downstairs">>
Pretend you didn't hear. Pretend you didn't lean in. Cloth shifts; a kiss you can't see. The hall feels too bright in the dark.
<</narrative>>
<</if>>
<<narrative "Downstairs">>
You peel yourself off the wall. Whatever you do next, it won't be *here*.
<</narrative>>
<div class="location-actions">
<<btn "Move on" "fhDownstairs">><</btn>>
</div>
<</nobr>><<nobr>>
<<set $location = "fhGarage">>
<<updateCharacterLocations>>
<<narrative "Garage">>
The family garage is cluttered but organized. Tools line the workbench along the back wall, and boxes of holiday decorations are stacked in the corner. The smell of oil, sawdust, and old cardboard hangs in the air.
<</narrative>>
<<showLocationChars "fhGarage">>
<<navMenu>>
<<navCard "FamilyHouse">>
<</navMenu>>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "fhKitchen">>
<<updateCharacterLocations>>
<</silently>>
<<narrative "Kitchen">>
The kitchen is warm and inviting, filled with the aroma of home cooking. Sunlight streams through the window.
<</narrative>>
<<showLocationChars "fhKitchen">>
<<questPrompts>>
<div class="location-actions"><<btn "Eat Food" "eatFood">><</btn>>
<<btn "Drink Water" "drinkWater">><</btn>>
<<btn "Coffee" "drinkCoffee">><</btn>>
<<showFamilyMealBtn>>
</div>
<<navMenu>>
<<navCard "fhKitchen">>
<<navCard "fhLivingroom">>
<<navCard "fhDownbath">>
<<navCard "fhParentsRoom">>
<<navCard "fhBackyard">>
<<navCard "fhGarage">>
<<navCard "fhUpperstairs">>
<<navCard "maplewood">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "fhLaundryRoom">>
<<updateCharacterLocations>>
<<narrative "Laundry Room">>
A small utility room off the upstairs hall. A washing machine and dryer sit beneath a shelf of detergent and fabric softener. A hamper waits in the corner.
<</narrative>>
<<showLocationChars "fhLaundryRoom">>
<<if ndef $wardrobe>><<set $wardrobe = {}>><</if>>
<<if ndef $wardrobe.laundryBasket>><<set $wardrobe.laundryBasket = []>><</if>>
<div class="location-actions">
<<if $wardrobe.laundryBasket.length > 0>>
<<btn "Wash laundry" "fhLaundryWash">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Your laundry basket is empty. Items are added when clothes get dirty while you spend time in your bedroom."><i class="icon icon-lock icon-12"></i> Wash laundry</span>
<</if>>
</div>
<<navMenu>>
<<navCard "fhUpperstairs">>
<</navMenu>>
<</nobr>><<silently>>
<<set $location = "fhLaundryRoom">>
<<advanceTime 55>>
<<washAllWardrobeItems>>
<<gainSkill "practical" "cleaning" 1>>
<<run window.notifySuccess("Laundry done")>>
<<flushNotifications>>
<<recalculateStats>>
<<set _laundryVidIndex = random(1, 3)>>
<</silently>>
<<narrative "Laundry Room">>
You sort lights and darks, check pockets, and load the washer. The machine hums and sloshes steadily while you tidy the shelf and fold a few stray towels from the dryer rack.
When the cycle finishes, you move everything to the dryer or the drying rack, then fold and put pieces away. Your clothes feel clean again.
<</narrative>>
<<if _laundryVidIndex == 1>>
<<vid "assets/content/scenes/maplewood/familyHouse/laundryroom/laundry1.mp4" "100%">>
<<elseif _laundryVidIndex == 2>>
<<vid "assets/content/scenes/maplewood/familyHouse/laundryroom/laundry2.mp4" "100%">>
<<elseif _laundryVidIndex == 3>>
<<vid "assets/content/scenes/maplewood/familyHouse/laundryroom/laundry3.mp4" "100%">>
<</if>>
<div class="location-actions">
<<btn "Done" "fhLaundryRoom">><</btn>>
</div>/* ==========================================
EVENT - Ask Dad for Money (Something Different arc)
Dad scene after mom-talk hallway bridge.
Dad is warm, but says no; wallet remains.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhLivingroom">>
<<advanceTime 15>>
<<advanceQuestStage "something_different">>
<</silently>>
<<narrative "Living Room">>
You're about to head upstairs when the sound from the living room stops you.
<</narrative>>
<<image "assets/content/locations/maplewood/familyHouse/fhLivingRoom.webp" "100%">>
<<narrative>>
The TV cuts out. Then comes your father's rough cough, the soft click of the remote hitting the coffee table.
<</narrative>>
<<narrative>>
You turn toward the room instead.
<</narrative>>
<<narrative>>
He's still sitting on the couch, half-turned. The TV is off now; the screen has gone dark. When he sees you, he looks up.
<</narrative>>
<<image "assets/content/scenes/interactions/father/postwork/level2/livingroomEvening-2.webp" "100%">>
<<dialog "father">>
Hey, sweetheart. Couldn't sleep?
<</dialog>>
<<dialog "player">>
No... um.
<</dialog>>
<<narrative>>
You stay there at the doorway for a second too long. He notices immediately. His face shifts - softer, more attentive.
<</narrative>>
<<dialog "father">>
What's wrong, honey?
<</dialog>>
<<dialog "player">>
I wanted to ask you for something... I just didn't know how to say it.
<</dialog>>
<<dialog "father">>
Come here. Tell me.
<</dialog>>
<<narrative>>
You take a couple of steps closer, but you don't fully sit down. Your hands stay clasped together.
<</narrative>>
<<dialog "player">>
It's a little embarrassing.
We just moved. Everything costs money. I know that.
But I need a little too. For myself... personal stuff. I talked to Mom. I kind of know where to start now, but... what I have isn't enough.
<</dialog>>
<<dialog "father">>
(After a short pause, still gentle.)
How much do you need?
<</dialog>>
<<dialog "player">>
Not that much, really. It's just that I barely have anything.
Cream, stuff for my face... things like that.
<</dialog>>
<<narrative>>
He doesn't answer right away. His fingers tap the edge of the coffee table once. There's no anger in him - just tiredness, and the look of someone doing numbers in his head.
<</narrative>>
<<dialog "father">>
Ah, sweetheart...
We're really tight right now.
The move, the missing things, the payments... it all landed at once. So I can't give you money right now.
<</dialog>>
<<narrative>>
He says it softly. Maybe he would if he could. Maybe he really can't.
<</narrative>>
<<narrative>>
It still sinks into you.
<</narrative>>
<<dialog "player">>
(Your voice hardens a little, but doesn't rise.)
Okay, but everything keeps turning into "later."
The suitcase is later. The missing things are later. What I need is later too.
<</dialog>>
<<dialog "father">>
Don't say that.
<</dialog>>
<<dialog "player">>
But that's what it feels like.
<</dialog>>
<<dialog "father">>
(He dips his head slightly, tired but calm.)
I know. I know it feels bad.
But that doesn't mean we're brushing you aside, okay? It just means... we can't keep up with everything all at once right now.
<</dialog>>
<<narrative>>
There's a brief silence.
<</narrative>>
<<narrative>>
Then your father stands. As he passes you, he rests a hand on your shoulder.
<</narrative>>
<<dialog "father">>
We'll deal with this too. Maybe not immediately... but we will, honey.
<</dialog>>
<<narrative>>
Then he leans down and kisses the top of your head.
<</narrative>>
<<narrative>>
Without saying anything else, he heads down the hall and disappears into his room.
<</narrative>>
<<image "assets/content/scenes/events/diana/walletTable.webp" "100%">>
<<narrative>>
The living room goes quiet.
<</narrative>>
<<narrative>>
His wallet is still on the coffee table. Thin. Dark leather. Right there in front of you.
<</narrative>>
<div class="location-actions">
<span class="link-internal btn-style btn-default locked" data-tooltip="You can't do that."><i class="icon icon-lock icon-12"></i> Take money from the wallet</span>
<<btn "Leave it alone" "fhLivingroom">><</btn>>
</div>
</div>/* ==========================================
EVENT - Close Dad's Wallet (Something Different arc)
Living room, dark; you walk away without taking anything.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhLivingroom">>
<<advanceTime 3>>
<</silently>>
<<narrative "Living Room">>
You set the wallet back down.
<</narrative>>
<<narrative>>
You pull your hand away.
<</narrative>>
<<narrative>>
It just sits there.
<</narrative>>
<<narrative>>
Bulging.
<</narrative>>
<<narrative>>
In the dark.
<</narrative>>
<<dialog "player">>
(Inner voice)
I could have.
Nobody was there.
<</dialog>>
<<narrative>>
You knew.
<</narrative>>
<<dialog "player">>
(Inner voice)
But I didn't.
<</dialog>>
<<narrative>>
You stand there and look at it for a long moment.
<</narrative>>
<<narrative>>
Then you turn away.
<</narrative>>
<div class="location-actions">
<<btn "That's enough." "fhLivingroom">><</btn>>
</div>
</div>/* ==========================================
EVENT - Take Money from Dad's Wallet (Something Different arc)
You take a bill and rush back upstairs.
Video: assets/content/scenes/events/diana/walletOnTable.mp4, walletTakeMoney.mp4
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhLivingroom">>
<<advanceTime 3>>
<<earnMoney 100 "You took $100 from Dad's wallet">>
<<gainStat "corruption" 1>>
<<flushNotifications>>
<</silently>>
<<narrative "Living Room">>
You pull one of the bills free.
<</narrative>>
<<narrative>>
Fast. Before you can think too hard about it.
<</narrative>>
<<vid "assets/content/scenes/events/diana/walletTakeMoney.mp4" "100%">>
<<narrative>>
You snap the wallet shut and put it back where it was.
<</narrative>>
<<narrative>>
The bill disappears into your pocket.
<</narrative>>
<<narrative>>
Your heart is pounding so hard it feels loud enough to fill the whole room.
<</narrative>>
<<dialog "player">>
(Inner voice)
Okay. Okay. Just get out.
<</dialog>>
<<narrative "Stairs">>
You head for the stairs without looking back.
<</narrative>>
<<narrative>>
You try to keep your steps quiet, but every one of them sounds too loud.
<</narrative>>
<<narrative>>
Your hand keeps drifting to your pocket, checking that the money is still there.
<</narrative>>
<<narrative "Upstairs">>
Just as you reach the top of the stairs, your brother's door swings open.
<</narrative>>
<<narrative>>
You flinch.
<</narrative>>
<<narrative>>
He steps into the hall, clearly on his way to the bathroom.
<</narrative>>
<<dialog "brother">>
Sis? You okay?
<</dialog>>
<<dialog "player">>
Uh... kitchen. Couldn't sleep. Heading back to my room. You?
<</dialog>>
<<dialog "brother">>
Bathroom.
<</dialog>>
<<dialog "player">>
Right... yeah. Me too.
<</dialog>>
<<narrative>>
You hear it as soon as it leaves your mouth.
Too fast. Too eager.
<</narrative>>
<<narrative>>
Your brother gives you a short look.
<</narrative>>
<<dialog "brother">>
Okay...
<</dialog>>
<<narrative>>
He keeps looking at you for another second.
<</narrative>>
<<dialog "brother">>
Weird.
<</dialog>>
<<narrative>>
He doesn't say anything else and walks past you toward the bathroom.
You don't linger.
<</narrative>>
<<narrative "Bedroom">>
The second you close the door behind you, you finally let out a breath.
<</narrative>>
<<image "assets/content/locations/maplewood/familyHouse/fhBedroom.webp" "100%">>
<<narrative>>
Your hand goes straight to your pocket.
<</narrative>>
<<narrative>>
The bill is still there.
<</narrative>>
<<narrative>>
Your stomach twists.
<</narrative>>
<<narrative>>
But you don't pull your hand away.
<</narrative>>
<<narrative>>
Now it feels real.
<</narrative>>
<div class="location-actions">
<<btn "Continue" "fhBedroom">>
<<silently>>
<<completeQuest "something_different">>
<</silently>>
<</btn>>
</div>
</div><<nobr>>
<<set $location = "fhLivingroom">>
<<updateCharacterLocations>>
<<narrative "Living Room">>
The living room is cozy and comfortable. A large TV dominates one wall, and soft couches invite you to relax.
<</narrative>>
<<showLocationChars "fhLivingroom">>
<<questPrompts>>
<div class="location-actions">
<<if $flags.firstWorkDayWalkHome>>
<<btn "Say hello" "event_fhLivingroom_firstWorkDayFamily" "action-btn btn-quest">><</btn>>
<</if>>
<<btn "Go to Couch" "fhCouch">><</btn>>
<<yogaActions>>
<<danceActions>>
</div>
<<navMenu>>
<<navCard "fhKitchen">>
<<navCard "fhLivingroom">>
<<navCard "fhDownbath">>
<<navCard "fhParentsRoom">>
<<navCard "fhBackyard">>
<<navCard "fhGarage">>
<<navCard "fhUpperstairs">>
<<navCard "maplewood">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "fhParentsBath">>
<<set $returnAfterToilet = "fhParentsBath">>
<<updateCharacterLocations>>
<<bathroomEntry "fhParentsBath" "Parents Bathroom" "fhParentsRoom">>
<<if !State.temporary.bathroomCheck.someoneShowering>>
/* No one showering - normal bathroom */
<<narrative "Parents Bathroom">>
A luxurious en-suite bathroom connected to the master bedroom. A large bathtub and marble counters speak of comfort.
<</narrative>>
<<showLocationChars "fhParentsBath">>
<div class="location-actions">
<<bathroomActions>>
<<mirrorBtn>>
</div>
<<navMenu>>
<<navCard "fhParentsRoom">>
<</navMenu>>
<</if>>
<</nobr>>/* ==========================================
EVENT - Hallway after mom talk (Something Different arc)
Bridge: parents' room → hallway / living room sounds → dad.
No quest advance (mother scene already advanced).
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhDownstairs">>
<<advanceTime 5>>
<</silently>>
<<narrative "Hallway">>
You close the door gently behind you.
<</narrative>>
<<narrative>>
The hallway feels cooler at once. A moment ago the room was warmher voice, the bed, her hands on your faceand now it's a few steps behind you.
<</narrative>>
<<narrative>>
As you walk, your fingers drift to your cheek without thinking.
<</narrative>>
<<narrative>>
"Keep it simple."
<</narrative>>
<<narrative>>
"Moisturizer."
<</narrative>>
<<narrative>>
"Sunscreen in the morning."
<</narrative>>
<<narrative>>
What she said still rings clear. For the first time, this doesn't feel completely out of reach.
<</narrative>>
<<narrative>>
Then the cost comes back.
<</narrative>>
<<narrative>>
The feeling breaks apart.
<</narrative>>
<<dialog "player">>
(Inner voice)
Moisturizer. Sunscreen. That's still money. And right now even small things feel bigger than they should.
<</dialog>>
<<narrative>>
You slow near the mouth of the hall. Talking to your dad shouldn't feel this heavy.
<</narrative>>
<<narrative>>
Tonight it does.
<</narrative>>
<<narrative>>
You're about to turn away when the TV in the living room cuts out.
<</narrative>>
<<narrative>>
Then your father's rough cough. The remote clicks onto the coffee table. The couch shifts with a soft creak.
<</narrative>>
<<narrative>>
You stop. He's still in there.
<</narrative>>
<<narrative>>
Footsteps creak toward the hallwayslow, like he's about to head for bed.
<</narrative>>
<<narrative>>
If he crosses that threshold before you do, the opening closes.
<</narrative>>
<<dialog "player">>
(Inner voice)
If I don't speak now, I'll put it off again.
<</dialog>>
<<narrative>>
Your hand brushes the wall.
You take a breath.
Then you head for the living room.
<</narrative>>
<div class="location-actions">
<<btn "Say hi to Dad" "fhLivingRoom_event_askDadMoney">><</btn>>
</div>
</div><<nobr>>
<<script>>
if (window.QuestSceneNav && typeof window.QuestSceneNav.scheduleHubAutoScene === "function") {
window.QuestSceneNav.scheduleHubAutoScene("fhParentsRoom");
}
<</script>>
<<set $location = "fhParentsRoom">>
<<updateCharacterLocations>>
<<narrative "Parents' Room">>
The master bedroom is elegant and well-kept. A large bed dominates the room, and soft curtains filter the light.
<</narrative>>
<<showLocationChars "fhParentsRoom">>
<<questPrompts>>
<<navMenu>>
<<navCard "fhParentsBath">>
<<navCard "fhDownstairs">>
<</navMenu>>
<</nobr>>/* ==========================================
EVENT - Talk to Mom (Something Different arc)
After research at brother's PC. Warm, practical, a little advice.
Voice: <<narrative>> to player (*you* ok); <<dialog>> for speech.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhParentsRoom">>
<<advanceTime 25>>
<<advanceQuestStage "something_different">>
<</silently>>
<<narrative "Parents Room">><</narrative>>
<<image "assets/content/locations/maplewood/familyHouse/fhParentsRoom.webp" "100%">>
<<narrative>>
She's lying back on the bed, book open in her lap, glasses perched on her nose. When she sees you, she marks the page and smiles.
<</narrative>>
<<dialog "player">>
Mom, are you busy? Am I interrupting?
<</dialog>>
<<dialog "mother">>
No, honey, you're not interrupting. Come hereI'm glad you came.
<</dialog>>
<<dialog "player">>
What were you doing?
<</dialog>>
<<dialog "mother">>
(She sets the book aside and sighs.)
Reading, really. I've been on house stuff all day, babylaundry, your things, the kitchen… I needed to clear my head. We haven't seen much of each other lately. Sit here.
<</dialog>>
<<narrative>>
She taps the edge of the bed.
<</narrative>>
<<image "assets/content/scenes/interactions/mother/level2/parentsroomEvening-1.webp" "100%">>
<<narrative>>
You sit beside her.
<</narrative>>
<<dialog "player">>
Long day?
<</dialog>>
<<dialog "mother">>
(Soft, wry.)
Very. But I'm all right. How are you? How's work? You look a little worn out, sweetieyour face seems pale.
<</dialog>>
<<dialog "player">>
Not terrible. I'm still getting used to it.
<</dialog>>
<<dialog "mother">>
Good. Don't forget to rest, okay?
<</dialog>>
<<narrative>>
A short silence. Then your voice comes out smaller than you meant.
<</narrative>>
<<dialog "player">>
Mom… I've been stuck in my head about how I look. Like I'm scanning for what's wrong before anyone else gets the chance.
<</dialog>>
<<dialog "mother">>
(She puts the book down completely, surprised.)
What? Where did that come from?
<</dialog>>
<<dialog "player">>
I don't know. It just got loud in there.
<</dialog>>
<<narrative>>
A beat. Her expression softens.
<</narrative>>
<<dialog "mother">>
Come here.
(She cups your face in her hands and tilts it gently.)
Of course you're pretty. What kind of question is that?
<</dialog>>
<<dialog "player">>
Actually… more than being pretty, I want to feel like I'm taking care of myself. Lately my face almost feels foreignmy skin, the way I look… And it tightens something inside.
<</dialog>>
<<dialog "mother">>
(She studies you, thumb brushing your jawnot judging.)
You don't look foreign, sweetie. That's tiredness, maybe a little dryness. It's not "you're not pretty." It's neglect, maybe. People do it without noticing.
<</dialog>>
<<dialog "player">>
What am I supposed to do? I looked things up online and only got more confusedevery video says something different, ten steps here, "just drink water" there… I didn't know who to trust. I told myself my mom would know best.
<</dialog>>
<<image "assets/content/scenes/interactions/mother/level2/parentsroomEvening-3.webp" "100%">>
<<dialog "mother">>
(She laughs.)
The internet? Of course it confused you.
You're lucky. In my day we didn't have all this informationwe figured things out by word of mouth, whatever worked.
<</dialog>>
<<dialog "mother">>
(She thinks for a moment.)
Start simple. I've got creams in the bathroommoisturizer, sunscreen in the morning… You can use mine if you want, but I'll be honest: they're not really picked for skin your age; they're for what I need. For now, a light moisturizer and sunscreen in the morning are enough. Start therewe'll talk about the rest when you're ready, no rush.
<</dialog>>
<<dialog "player">>
Okay…
<</dialog>>
<<dialog "mother">>
(She tucks your hair behind your ear.)
Drink water. Sleep. That counts as care too, don't forget. You're such a beautiful girl.
<</dialog>>
<<dialog "player">>
You always look like you have it together. How do you do it? Dad still looks at you like that.
<</dialog>>
<<dialog "mother">>
(Her eyes soften; she smiles, far away for a second.)
Your father… he's such a goofball. I mean that in the best way.
In college it was a friend's birthday. I'd done my hair, a little makeup… He barely talked to me all night. Just watched.
<</dialog>>
<<dialog "player">>
And then?
<</dialog>>
<<dialog "mother">>
(She pauses, smiling but holding something back.)
That's not really tonight's topic, sweetiebut in short, life sped up. Home, work, you kids… That's a long story for another night, or we'll be here till morning.
<</dialog>>
<<dialog "player">>
So… love at first sight, huh?
<</dialog>>
<<narrative>>
You both laughlight, easy.
<</narrative>>
<<dialog "mother">>
But I'll say this clearly: someone who truly values you doesn't only look at your facethey look at you inside. Beauty gets attention, yes. But it doesn't hold anyone on its own.
<</dialog>>
<<dialog "player">>
What about wanting attention?
<</dialog>>
<<dialog "mother">>
(Smiling, but serious underneath.)
That's not a bad thing either, honey. People want to be seen sometimes. Just be careful who you're pulling inwhat you settle for, where you draw the line. Don't shrink yourself. Don't put yourself at risk either. Okay?
<</dialog>>
<<dialog "player">>
Okay.
<</dialog>>
<<dialog "mother">>
Good. Now come here and give me a hug.
<</dialog>>
<div class="location-actions">
<<btn "Step out" "fhParentsHall_afterMotherTalk">><</btn>>
</div>
</div><div class="divnobr">
<<set $location = "fhUpperBath">>
<<set $returnAfterToilet = "fhUpperBath">>
<<updateCharacterLocations>>
<<bathroomEntry "fhUpperBath" "Upstairs Bathroom" "fhUpperstairs">>
<<if !State.temporary.bathroomCheck.someoneShowering>>
/* No one showering - normal bathroom */
<<narrative "Upstairs Bathroom">>
A clean, shared bathroom with a shower and basic amenities. The mirror reflects the morning light.
<</narrative>>
<<showLocationChars "fhUpperBath">>
<div class="location-actions">
<<btn "Take a Shower" "useBath">><</btn>>
<<bathroomActions>>
<<mirrorBtn>>
</div>
<<navMenu>>
<<navCard "fhUpperstairs">>
<</navMenu>>
<</if>>
</div><<nobr>>
<<script>>
if (window.QuestSceneNav && typeof window.QuestSceneNav.scheduleHubAutoScene === "function") {
window.QuestSceneNav.scheduleHubAutoScene("fhUpperstairs");
}
<</script>>
<<set $location = "fhUpperstairs">>
<<updateCharacterLocations>>
<<narrative "Upstairs">>
The upstairs hallway is quiet and peaceful. Family photos line the walls, and soft carpet muffles your footsteps.
<</narrative>>
<<showLocationChars "fhUpperstairs">>
<<navMenu>>
<<navCard "fhBedroom">>
<<navCard "fhBrotherRoom">>
<<navCard "fhUpperBath">>
<<navCard "fhLaundryRoom">>
<<navCard "fhDownstairs">>
<</navMenu>>
<</nobr>>/* ==========================================
EVENT - Wallet Chance (Something Different arc)
Late-night temptation after mall aftermath
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhUpperstairs">>
<<advanceTime 8>>
<</silently>>
<<narrative>>
You can't sleep.
You head downstairs thinking maybe there's something to snack on in the kitchen.
<</narrative>>
<<narrative>>
Halfway down the stairs, you hear your father's voice from below. Short, tired, almost under his breath.
<</narrative>>
<<narrative>>
The second you step into the living room, the door at the end of the hall closes.
A soft click.
<</narrative>>
<<dialog "player">>
(Inner voice)
He must be just going to bed...
<</dialog>>
<<narrative>>
You start toward the kitchen in the dim light.
A few steps later, your foot bumps into something.
<</narrative>>
<<dialog "player">>
Shit... what did I knock over?
<</dialog>>
<<narrative>>
You look down and realize you hit one of your dad's slippers.
<</narrative>>
<<image "assets/content/scenes/events/diana/slippersHit.webp" "100%">>
<<narrative>>
You crouch and quietly set it straight.
When you stand back up, your eyes catch on the wallet sitting on the table.
Bulging. Not fully closed.
<</narrative>>
<<image "assets/content/scenes/events/diana/walletTable.webp" "100%">>
<<narrative>>
You can make out the money inside through the gap.
You stop.
Something he said earlier catches in your head.
"We're tight right now."
<</narrative>>
<<dialog "player">>
(Inner voice)
Whoa... I thought he didn't have money.
<</dialog>>
<<narrative>>
Without meaning to, you take a step closer to the wallet.
<</narrative>>
<<dialog "player">>
(Inner voice)
What if someone comes out...
<</dialog>>
<<narrative>>
You glance down the hall.
No one.
Your hand reaches out.
Your fingers touch the wallet.
You ease it open.
<</narrative>>
<<narrative>>
There are hundred-dollar bills stacked inside.
Not one or two.
<</narrative>>
<<dialog "player">>
Did my dad lie to me...?
<</dialog>>
<<narrative>>
You press your lips together.
For a second, all you do is stare at the money.
Maybe it really is for something else.
The house. Bills. The move.
But that doesn't change what you're looking at.
The money is right here.
<</narrative>>
<<narrative>>
Your fingers drift toward the bills.
Your heart starts beating faster.
You glance toward the hall again.
The house is quiet.
Too quiet.
<</narrative>>
<<dialog "player">>
(Inner voice)
If I took just one, would he even notice?
<</dialog>>
<div class="location-actions">
<<btn "Take money" "fhLivingRoom_event_stealDad">><</btn>>
<<btn "Close the wallet" "fhLivingRoom_event_closeWallet">><</btn>>
</div>
</div><<nobr>>
<<set $location = "fhWardrobe">>
<</nobr>>
<<wardrobe>><<nobr>>
<<set $location = "floorGround">>
<<updateCharacterLocations>>
<<narrative "Ground Floor">>
The main floor of the mall features clothing stores, electronics, and sports equipment. Escalators lead to the upper floors.
<</narrative>>
<<showLocationChars "floorGround">>
<div class="location-actions">
<<set $mallWindowShoppingReturn = "floorGround">>
<<btn "Window Shopping" "mall_windowShopping" "default" 15>><</btn>>
</div>
<<navMenu>>
<<navCard "storeClothingA">>
<<navCard "storeCosmetics">>
<<navCard "storeElectronics">>
<<navCard "storeShoeA">>
<<navCard "storeSports">>
<<navCard "storeSwim">>
<<navCard "mallBathroomFromGround">>
<<navCard "floorSecond">>
<<navCard "floorThird">>
<<navCard "downTown">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "floorSecond">>
<<updateCharacterLocations>>
<<narrative "Second Floor">>
The second floor offers fashion boutiques, jewelry stores, and intimate apparel shops. High-end brands line the corridors.
<</narrative>>
<<showLocationChars "floorSecond">>
<div class="location-actions">
<<set $mallWindowShoppingReturn = "floorSecond">>
<<btn "Window Shopping" "mall_windowShopping" "default" 15>><</btn>>
</div>
<<navMenu>>
<<navCard "storeBags">>
<<navCard "storeClothingB">>
<<navCard "storeClothingC">>
<<navCard "storeJewelry">>
<<navCard "storeLingerieA">>
<<navCard "storeLingerieB">>
<<navCard "storeShoeB">>
<<navCard "mallBathroomFromSecond">>
<<navCard "floorGround">>
<<navCard "floorThird">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "floorThird">>
<<updateCharacterLocations>>
<<narrative "Third Floor">>
The top floor houses the food court, movie theatre, and beauty services. The aroma of various cuisines fills the air.
<</narrative>>
<<showLocationChars "floorThird">>
<div class="location-actions">
<<set $mallWindowShoppingReturn = "floorThird">>
<<btn "Window Shopping" "mall_windowShopping" "default" 15>><</btn>>
</div>
<<navMenu>>
<<navCard "foodCourtMall">>
<<navCard "salonBeautyMall">>
<<navCard "theatreMovie">>
<<navCard "mallBathroomFromThird">>
<<navCard "floorGround">>
<<navCard "floorSecond">>
<</navMenu>>
<</nobr>><<nobr>><<set $location = "foodCourtMall">><</nobr>>
<<updateCharacterLocations>>
<<restaurant "foodCourtMall" "floorThird">><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<!-- Sidebar -->
<div class="prologue-sidebar">
<div class="prologue-sidebar-title">My Selections</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Early Years (0-5)</div>
<<if $prologueSelections.early>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.early.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.early.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Childhood (6-9)</div>
<<if $prologueSelections.childhood>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.childhood.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.childhood.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title current">Formative (10-12)</div>
<<if $prologueSelections.formative>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.formative.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.formative.effect</div>
</div></div>
<<else>>
<div class="prologue-sidebar-empty">No selection yet</div>
<</if>>
</div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Adolescent (13-15)</div></div>
<div class="prologue-sidebar-section"><div class="prologue-sidebar-section-title pending">Coming of Age (16-17)</div></div>
</div>
<div class="prologue-content-area">
<div class="prologue-progress-container">
<div class="prologue-progress-bar">
<div class="prologue-progress-fill" style="width: 50%;"></div>
</div>
<div class="prologue-progress-steps">
<span class="prologue-progress-step completed">0-5</span>
<span class="prologue-progress-step completed">6-9</span>
<span class="prologue-progress-step active">10-12</span>
<span class="prologue-progress-step">13-15</span>
<span class="prologue-progress-step">16-17</span>
</div>
</div>
<div class="prologue-header">
<div class="prologue-age-badge">Ages 10-12</div>
<h1 class="prologue-title">Growing Up</h1>
<div class="prologue-header-line"></div>
<p class="prologue-subtitle">Too old for cartoons, too young for everything else.</p>
</div>
<<image "assets/content/scenes/prologue/formativeYears.webp" "100%">>
<<narrative>>
Middle school. The worst three years of anyone's life.
<</narrative>>
<<narrative>>
Your body was changing. The kid who sat next to you in fourth grade was now six inches taller and played guitar. Everyone had a thing. You didn't.
<</narrative>>
<<narrative>>
At home, things were shifting.
<</narrative>>
<<narrative>>
Mom started treating you differently. "You're ten now," she'd say, asking your opinion on dinner plans, letting you stay up later, giving you more responsibility. But with that came expectations. "Set the table." "Help your brother with homework." "Clean your room without being asked."
<</narrative>>
<<narrative>>
One afternoon, you forgot to do the dishes. She didn't yell, just looked disappointed. "I thought I could count on you," she said quietly. That hurt more than any lecture.
<</narrative>>
<<narrative>>
Dad was around less. Work was demanding, he'd come home exhausted, barely said more than "how was school?" before retreating to the TV. You missed the weekend games in the backyard, the homework help at the kitchen table. When you tried to show him somethinga test score, a drawing, anythinghe'd nod, say "that's great, honey," but you could tell he wasn't really there.
<</narrative>>
<<narrative>>
One night at dinner, you finally snapped. "You never listen anymore!"
<</narrative>>
<<narrative>>
The table went quiet. Dad looked up, surprised. Mom touched his arm.
<</narrative>>
<<narrative>>
"You're right," he said after a moment. "I'm sorry. Work's been... but that's not an excuse." He put down his fork. "Tell me. What did you want to show me?"
<</narrative>>
<<narrative>>
That conversation didn't fix everything, but it was a start.
<</narrative>>
<<narrative>>
Your brother was changing too. He was eight, nine, tendeveloping his own personality, his own opinions. Sometimes you got along great, teaming up against your parents' rules, sharing inside jokes. Other times you foughtover the bathroom, the TV remote, whose turn it was to take out the trash.
<</narrative>>
<<narrative>>
One night, after a particularly bad fight, mom sat you both down. "You two are family," she said firmly. "Whatever happens, you have each other. Remember that."
<</narrative>>
<<narrative>>
You rolled your eyes. Your brother crossed his arms. But later, when he knocked on your door to apologize, you let him in.
<</narrative>>
<<narrative>>
School was harder now. More homework, serious tests, teachers saying "you're growing up now, time to get serious." But you were also more freeclubs, activities, hanging out with friends, making your own choices.
<</narrative>>
<<narrative>>
Those years shaped who you were becoming. You were starting to understand yourself, even if you didn't have all the answers yet.
<</narrative>>
<div class="prologue-memory-section">
<div class="prologue-memory-section-title">Character-Shaping Memories</div>
<div class="prologue-memory-cards" id="formative-cards">
<div class="prologue-memory-card" data-section="formative" data-title="Dad's Coaching" data-effect="+15% Fitness" data-stat="fitness" data-value="1.15">
<div class="prologue-memory-title">Dad's Coaching</div>
<div class="prologue-memory-text">
"You'd joined a sports team. Three practices a week, games on weekends. Dad started coming to watch. At first just occasionally, then every game. He'd stand on the sidelines, cheering loudly. After tough losses, he'd take you for ice cream. Strawberry. Always strawberry. 'You played great,' he'd say. 'I'm proud of you.' Mom would joke: 'Finally, something gets him off the couch.' You stopped telling him when a game got cancelled. He'd already taken off work."
</div>
<div class="prologue-memory-effect">+15% Fitness</div>
</div>
<div class="prologue-memory-card" data-section="formative" data-title="Mom's Pride" data-effect="+15% Intelligence" data-stat="intelligence" data-value="1.15">
<div class="prologue-memory-title">Mom's Pride</div>
<div class="prologue-memory-text">
"School became your arena. Science fairs, math competitions, honor roll every semester. Mom was your biggest cheerleader. She'd quiz you before tests, celebrate every achievement. 'You're so smart,' she'd say, showing off your report card to relatives. When you won that science fair, she cried. Dad was proud too, but mom saw you becoming something more. 'You're going to do great things,' she'd whisper. You weren't sure you agreed with her. But you kept studying, partly because the answer mattered and partly because you didn't want to find out."
</div>
<div class="prologue-memory-effect">+15% Intelligence</div>
</div>
<div class="prologue-memory-card" data-section="formative" data-title="Finding Your Thing" data-effect="+15% Physical Skills" data-stat="physical" data-value="1.15">
<div class="prologue-memory-title">Finding Your Thing</div>
<div class="prologue-memory-text">
"Dance. Gymnastics. Basketball for three weeks. You tried everything, searching for what fit. Some activities lasted weeks, others just days. Dad would drive you, wait in the car or watch from the stands. 'It's okay if you don't like it,' he'd say after you quit something. 'We'll find your thing.' Your brother would tag along sometimes, imitating your moves. You kept a list on the back of a notebook: everything you'd tried, with a check or an X next to each. The Xs outnumbered the checks. You kept adding to the list anyway."
</div>
<div class="prologue-memory-effect">+15% Physical Skills</div>
</div>
<div class="prologue-memory-card" data-section="formative" data-title="Your Own Path" data-effect="+15% Creative Skills" data-stat="creativity" data-value="1.15">
<div class="prologue-memory-title">Your Own Path</div>
<div class="prologue-memory-text">
"You'd picked up a hobby. An instrument, or drawing, or writing. You chose it yourself, not because mom or dad suggested it. They supported it anyway. Mom bought you supplies. Dad listened to you play, even when you were terrible. Your brother would barge in while you practiced. 'Can I try?' Sometimes you'd let him, teaching what you'd learned. You were bad at it for a long time. Then less bad. At some point you noticed that being bad at it had stopped bothering you, which felt like the real thing you'd learned."
</div>
<div class="prologue-memory-effect">+15% Creative Skills</div>
</div>
</div>
</div>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-secondary">Back</span>' "childhoodYears">><</link>>
<span id="formative-continue" class="prologue-btn prologue-btn-primary disabled">Continue</span>
</div>
</div>
</div>
</div>
</div>
<</nobr>>
<<script>>
$(document).ready(function() {
$('#formative-cards .prologue-memory-card').on('click', function() {
var $card = $(this);
var title = $card.data('title');
var effect = $card.data('effect');
var stat = $card.data('stat');
var value = $card.data('value');
$card.siblings().removeClass('selected');
$card.addClass('selected');
State.variables.prologueSelections.formative = {
title: title,
effect: effect,
stat: stat,
value: value
};
// Update sidebar - find the current section
var $currentSection = $('.prologue-sidebar-section-title.current').parent();
$currentSection.find('.prologue-sidebar-empty').remove();
$currentSection.find('.prologue-sidebar-item').remove();
$currentSection.append(
'<div class="prologue-sidebar-item"><div>' +
'<div class="prologue-sidebar-item-text">' + title + '</div>' +
'<div class="prologue-sidebar-item-effect">' + effect + '</div>' +
'</div></div>'
);
$('#formative-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('adolescentYears');
});
});
var existing = State.variables.prologueSelections.formative;
if (existing) {
$('#formative-cards .prologue-memory-card').each(function() {
if ($(this).data('title') === existing.title) {
$(this).addClass('selected');
$('#formative-continue')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
Engine.play('adolescentYears');
});
}
});
}
});
<</script>>
/* =============================================================================
FOTOGRAM – COMMENT NICKNAMES & COMMENT TEXT POOLS (by post flag)
=============================================================================
Loaded by: createFotogramCommentForPost() in phone-fotogram.js
Uses: setup.fotogramCommentNames → random author pick
setup.fotogramCommentTemplates → flag-keyed text arrays
Flag priority (getBestFlagMatch): spicy > bikini > risky > hips > ass > boobs > hot > teasing > default
Gender tone distribution: ~70% male / ~30% female
============================================================================= */
/* ── Comment author display names (100+) ─────────────────────────────────── */
<<set setup.fotogramCommentNames = [
"darkwolf_x",
"jake97",
"neonknight",
"maria.vibes",
"thickboy_rick",
"luna.eclipse",
"savage.mike",
"prettypoisonn",
"ghostrider_22",
"babygirl.maya",
"kingcobra",
"velvetdusk",
"rawdog_randy",
"cherry.lola",
"nighthawk.z",
"thatguy_marcus",
"pinkvelvet99",
"dom.hunter",
"sweet.nina",
"blaze_demon",
"hotboy.chris",
"diva.diana",
"real_tommy",
"jasmine.xx",
"alphamale_01",
"misty.rains",
"big.tony",
"sofia_dreams",
"draco.flames",
"queen.beeee",
"wolf.pack.7",
"babyj.xo",
"rocco_steele",
"peach.fuzz.99",
"the.don.v2",
"natasha.ok",
"ricky.raw",
"candycrush.x",
"zack_attack",
"layla.mood",
"beast.mode.on",
"bella.darling",
"johnny.sins.fan",
"lilith.after",
"chad.thunder",
"rosie.red",
"maxpower_69",
"ruby.gloom",
"steelrod.k",
"angel.dust.x",
"bruno.real.1",
"daisy.haze",
"mr.worldwide",
"lana.dreams",
"six.pack.sam",
"goddess.ivy",
"hectorblaze",
"moonchild.xx",
"diesel.dave",
"violet.sin",
"carlos.fuego",
"emma.xoxo",
"bigdaddy.t",
"sugarplum.3",
"troy.savage",
"kiki.glow",
"hammer_time_x",
"chloe.bites",
"viper.fang",
"scarlett.mood",
"ace.high.99",
"mia.midnight",
"rex.domino",
"bambi.eyes",
"stone_cold_j",
"pixie.dust.xx",
"bull.shark",
"sapphire.k",
"spike.rush",
"nadia.lust",
"maverick.one",
"honey.trap.x",
"dante.inferno",
"coco.butter",
"slash.burn",
"freya.wild",
"knuckles.88",
"starlight.bby",
"razor.edge.x",
"elena.sin",
"predator.v",
"dollface.99",
"ronin.blade",
"sassy.sarah",
"titan.grip",
"lola.bunny.x",
"phantom.kd",
"amber.waves",
"wardog.1",
"velvet.lips",
"chaos.king",
"juicy.jade",
"gunner.x",
"tiffany.raw",
"cobra.strike",
"peaches.n.cr",
"rampage.r",
"cherry.bomb.x"
]>>
/* ── Comment text pools by post flag ─────────────────────────────────────── */
<<run setup.fotogramCommentTemplates = {
/* ══════════════════════════════════════════════════════════════════════════
DEFAULT (casual / safe posts)
══════════════════════════════════════════════════════════════════════ */
default: [
"Looking great! 🔥",
"Love this shot.",
"This is so good.",
"Keep posting please!",
"Amazing vibe ✨",
"So good.",
"Need more of this.",
"You're glowing in every post 💫",
"Okay I'm hooked on your feed now.",
"Underrated account honestly.",
"Instant like, no question.",
"Everything about this is right.",
"You just keep getting better.",
"Stop being so perfect wtf 😩",
"Obsessed with your aesthetic.",
"This pic lives in my head rent free.",
"Feed goals, seriously.",
"I swear you never miss.",
"How are you even real?",
"Perfection doesn't exist. Oh, wait.",
"New follower here. Not disappointed. 👀",
"Girl you are stunning 💅",
"Bro she's unreal.",
"Can you just post every day please?",
"This whole feed is a vibe.",
"My favorite account no cap.",
"You have no idea how good you look.",
"I keep coming back to this pic lol.",
"Someone give this woman a modeling contract.",
"Gorgeous as always 💕",
"The vibes are immaculate.",
"Main character energy fr.",
"Not me refreshing your page every hour 😅",
"You're the reason I open this app.",
"This one's going in my saved folder.",
"Pretty doesn't even cover it.",
"Wow. Just wow.",
"Queen shit 👑",
"You woke up and chose to bless our feeds.",
"Okay miss thing, do your thing 💃",
"Why does every photo look like a magazine shoot?",
"Effortlessly beautiful.",
"Yep. Still the prettiest girl on here.",
"I can't scroll past without liking.",
"How do you make everything look so good?",
"Killing it as usual 🔥🔥",
"You have the best feed on this app. Period.",
"Natural beauty hits different.",
"This is art honestly.",
"Somebody stop her she's too powerful 😤",
"I showed this to my girl and she agrees. 10/10.",
"That smile is everything tho.",
"Flawless ✨",
"Every time I see your posts I smile ngl.",
"You should be famous by now fr.",
"This is the quality content I follow for.",
"Clean aesthetic. Love it.",
"She really said effortless beauty huh.",
"Another day another slay 💅",
"Can't get enough of your content.",
"You make it look so easy.",
"Radiating confidence and I'm here for it.",
"I didn't know perfect existed until this feed.",
"Bro she just doesn't miss 🎯",
"10 out of 10. Every. Single. Time.",
"I stg you get prettier every post.",
"My whole timeline needed this.",
"I don't usually comment but damn.",
"There she is. The moment we've been waiting for.",
"Your vibe is infectious fr.",
"This is giving everything it needs to give.",
"Just casually being the most beautiful person alive.",
"Girl HOW 😩✨",
"The glow up is real.",
"You've got that IT factor fr.",
"I screenshot all your posts don't judge me 😂",
"Drop the skincare routine sis.",
"Whoever's taking these photos is lucky.",
"I would sell my soul for this aesthetic.",
"She just has that aura. Can't explain it."
],
/* ══════════════════════════════════════════════════════════════════════════
SPICY (provocative / suggestive posts)
══════════════════════════════════════════════════════════════════════ */
spicy: [
"You are unreal 🔥",
"Okay this one is fire.",
"Can we talk about this post? wow.",
"Too good honestly.",
"This hit different.",
"I was NOT ready for this one 😳",
"You woke something up in me ngl.",
"Bro I dropped my phone.",
"This should come with a warning label ⚠️",
"My heart just stopped for a sec.",
"You're doing god's work with these posts.",
"This is criminal. In a good way.",
"Who gave you permission to be this fine?",
"I'm sweating and it's not even hot outside 🥵",
"Had to screenshot, sorry not sorry.",
"This pic is a hazard to my health.",
"I need a moment after this one.",
"You literally broke the internet with this.",
"Every time you post I lose brain cells.",
"The things I'd do... respectfully. 😏",
"My jaw is still on the floor.",
"You're a menace and I love it.",
"This should be framed in a museum.",
"I zoomed in. Multiple times. No regrets.",
"If looks could kill, I'd be dead twice.",
"Fuck. That's the whole comment. Just fuck. 🥵",
"I just choked on my water bro.",
"This is NOT okay for my blood pressure.",
"She woke up and chose violence today.",
"Imagine being THIS attractive. Can't relate.",
"My girl saw me looking at this and now I'm single 😂",
"Please stop ruining other women for me.",
"You have no idea what this does to me.",
"I need to go take a cold shower real quick.",
"God really has favorites huh.",
"This pic just attacked me and I liked it.",
"My phone screen got hot from this post 🔥🔥",
"Nah this is disrespectful. In the best way.",
"I've been staring for 10 minutes straight.",
"GIRL. You're dangerous. 😈",
"I showed this to my friends and they all went silent.",
"This should be illegal in at least 12 states.",
"Someone check on me after this post.",
"This is why I have trust issues with my self-control.",
"You just ruined my whole week. In a good way.",
"How do you keep one-upping yourself???",
"My feed was boring until you showed up.",
"Literally gasped out loud. Thanks for that.",
"I'll be thinking about this at 3am. Guaranteed.",
"This is not the app for what you're doing to me rn 😩",
"If I die looking at your posts tell my mom I lived well.",
"Bro she is a fucking weapon.",
"This pic has me questioning my whole life.",
"You should charge people to look at this fr.",
"They need to put a NSFW tag on your whole account.",
"I lost my shit when this popped up on my feed.",
"I swear you're getting hotter every day. How??",
"I'm simping and I don't even care anymore.",
"The audacity of being this sexy.",
"Okay I'm obsessed and I'm not ashamed to say it.",
"This is a masterpiece of thirst.",
"Bruh I need oxygen after this 💀",
"You could ruin my life and I'd say thank you.",
"New religion just dropped and it's your feed.",
"I was having a bad day and then I saw this. Thank you.",
"I can feel my IQ dropping looking at this. Worth it.",
"How does a single image make me feel this way.",
"I'm not down bad I'm down CATASTROPHIC 😫",
"You're the reason they invented the heart eyes emoji.",
"My screen is fogging up rn.",
"Okay mommy. I'll behave. 🧎",
"The way I RAN to the comments.",
"I would risk it all for this post.",
"Every man in your DMs right now is panicking 😂",
"Bestie you ATE this. Left no crumbs. 🔥💅",
"I'm a straight girl and even I can't handle this.",
"Ma'am this is a public platform 😳😏",
"This post just stole my whole soul.",
"Not me saving this for... later. 👀",
"Sheesh. That's the comment. Just sheesh.",
"I had to lock my door before opening this 💀",
"My DMs are open btw. Just putting that out there.",
"She posted this at 1am knowing what she was doing.",
"I'm not gonna survive your next post.",
"This pic just became a core memory.",
"She's a whole problem and I love problems. 😈",
"I need to go to church after seeing this.",
"My WiFi slowed down loading all this heat 🔥",
"I'm supposed to be working rn and you post THIS?",
"The comment section is about to be a mess because of you 😂"
],
/* ══════════════════════════════════════════════════════════════════════════
BIKINI (swimwear / beach / pool posts)
══════════════════════════════════════════════════════════════════════ */
bikini: [
"Beach energy is perfect 🏖️",
"That look suits you so much.",
"Summer mood unlocked ☀️",
"You glow.",
"Body of a goddess, wow.",
"That bikini was MADE for you.",
"You make the ocean jealous.",
"Okay beach babe alert 🚨",
"The water isn't the only thing that's wet 💦",
"Absolute smoke show by the pool.",
"I suddenly need a vacation.",
"You just made summer my favorite season.",
"Damn that tan line though 👀",
"I want to be that bikini rn.",
"Sun-kissed and deadly.",
"That body is insane. Just saying.",
"You're making everyone at the beach look bad.",
"I forgot how to breathe for a second.",
"This should be on a billboard.",
"Whoever took this photo is the real MVP.",
"That bikini is holding on for dear life 😂",
"Vitamin sea and vitamin SHE 🌊",
"How is your body even real.",
"The beach doesn't deserve you honestly.",
"Baywatch who?? You're the upgrade.",
"I'm booking a flight to wherever you are rn ✈️",
"You make water look dry in comparison. You're dripping.",
"Sports Illustrated called. They want you.",
"That swimsuit is screaming for mercy lol.",
"Beach goddess. That's it. That's the comment. 🔱",
"I need CPR after seeing this.",
"Imagine pulling up to the beach and seeing THIS.",
"Girl the BODY 😩🔥",
"I stared at this so long my phone went to sleep.",
"You are the reason sunscreen sales go up.",
"That tan + that body = lethal combo.",
"I would drown on purpose just so you could save me.",
"Fuck me that bikini is working HARD 🥵",
"The audacity to look this good in a two-piece.",
"My summer is ruined because no beach has YOU on it.",
"That body needs its own insurance policy.",
"Okay but the way the water drips down... 💧",
"I was thinking about going to the gym and now I know why.",
"The sand is lucky to be under those feet.",
"You just gave every girl at the pool insecurity issues.",
"Nah she's a whole mermaid bro. 🧜♀️",
"I bet the lifeguard can't focus with you around 😂",
"That bikini body is not from this planet.",
"POV: you see a goddess at the beach.",
"I can't even look at this in public rn.",
"The waves aren't the only thing making me wet. Sorry. 💀",
"If I saw you at the pool I'd walk into a pole.",
"This pic is the definition of summer heat 🔥",
"That ass in that bikini? Ungodly.",
"Bestie you SNAPPED. Beach queen fr. 👑🏖️",
"I'm a girl and even I'm staring omg 😅",
"You look like a whole damn vacation.",
"I think my AC just broke looking at this.",
"How does one human contain all that beauty?",
"The way that bikini fits... chef's kiss 🤌",
"I hate you for making me feel this single rn 😂",
"I showed my boyfriend and now he won't give my phone back.",
"Respectfully... disrespectful thoughts only. 🙏🥵",
"Malibu Barbie but make it real.",
"Pool party? More like pool SLAYER.",
"You ate. You devoured. You left nothing.",
"Seriously how do I get that body asking for a friend.",
"The Pacific Ocean called, it wants its depth back.",
"Wettest post on my feed and it's not even raining 💦",
"I've seen this 47 times and I'm not done.",
"She just casually ended everyone's beach pics.",
"Bruh tag this as NSFW my boss almost saw 😂",
"That bikini is clinging on tighter than my ex.",
"If this is what summer looks like, I never want winter.",
"Literal perfection in swimwear.",
"I stg that bikini bottom is barely hanging on 👀",
"That body + that water = my new screensaver.",
"I'm moving to whatever beach you're at. Immediately.",
"My pool suddenly looks boring compared to yours.",
"She makes every beach an A-list beach just by showing up.",
"Dripping wet and dripping fine. Double drip. 💦🔥",
"Girl drop the workout routine because WHAT 😩💪",
"I need to cool down and this pic ain't helping."
],
/* ══════════════════════════════════════════════════════════════════════════
RISKY (daring / bold / pushing-limits posts)
══════════════════════════════════════════════════════════════════════ */
risky: [
"Bold post, respect. 🫡",
"You really own your style.",
"Not everyone can pull this off.",
"You're braver than most. And hotter.",
"This is the content I signed up for.",
"Living on the edge and looking good doing it.",
"You just raised the bar for everyone.",
"I gasped when I saw this. For real.",
"This is what confidence looks like. 💪",
"Okay you're not playing around anymore.",
"I love that you don't give a fuck.",
"Risky? More like perfect.",
"You just made half this app jealous.",
"This is bold and I'm absolutely here for it.",
"Pushing limits and looking flawless.",
"I can't believe you actually posted this. Legend. 👑",
"You woke up and chose violence today.",
"My feed needed this energy.",
"This pic has more guts than most people.",
"Dangerous post. Dangerous woman. 😈",
"The balls on this post. Respect.",
"She really said 'fuck the rules' and hit post.",
"This is why you're different from everyone else.",
"You just broke every unwritten rule and I love it.",
"How does it feel to be the bravest person on this app?",
"My timeline was dead until this showed up.",
"Okay I see you. You're not holding back anymore. 🔥",
"This is either insane or genius. Probably both.",
"Girl the CONFIDENCE. I'm obsessed. 💅",
"If you're gonna be bold, be this bold.",
"Every prude on this app just clutched their pearls lol.",
"You are fearless and it's so fucking attractive.",
"This post just slapped me in the face. Thank you.",
"I bet your DMs are a warzone rn 😂",
"This takes guts. And you've got plenty.",
"She does not give a single fuck and that's hot.",
"Boundary? What boundary? She doesn't know her.",
"I would NEVER have the nerve. Queen shit.",
"Half the girls on here wish they could do this.",
"You're built different. Mentally and physically. 💀🔥",
"I screamed when I saw this no joke.",
"Okay she's officially my hero.",
"Bestie you really woke up and said 'watch this' huh 😩",
"The risk is what makes it so damn hot.",
"This post is giving unhinged energy and I stan.",
"She went there. She actually went there.",
"I need to sit down after seeing this.",
"You have absolutely zero chill and I respect it.",
"If this gets deleted just know I saw it first 👀",
"This is the bravest post I've ever liked.",
"She treats this app like she owns it. As she should.",
"I can't believe my eyes but I don't want to look away.",
"What a time to be alive and following this account.",
"The audacity. The nerve. The BODY. 🔥",
"I wish I had 1% of this confidence.",
"You just gave every girl on here something to aspire to.",
"Not a single fuck was given and it shows.",
"This is art. Controversial art. But art.",
"She's not pushing the envelope. She's shredding it.",
"This is the energy I want in my life.",
"Absolute power move right here. 💪",
"I stg if they delete this I'm rioting.",
"This hit so different at 2am ngl 😏",
"You said 'who's stopping me' and the answer is NOBODY.",
"I had to double-check I was on the right app 😂",
"GIRL. The COURAGE. Stand up. 👏",
"I live for women who don't give a shit. Icon.",
"Riskiest post on my feed. Also the best. Coincidence?",
"She's playing a game the rest of us are scared to enter.",
"My heart rate went up seeing this.",
"Report this? Nah. Reward this. 🏆",
"Unapologetically bold. That's the brand.",
"Every hater in the comments can eat it. This is fire.",
"She went from 0 to 100 and we're all along for the ride.",
"I gasped, I stared, I saved. In that order.",
"She really has no limit huh. Iconic. 😈",
"I'm scared and turned on at the same time.",
"The internet wasn't ready for this. Neither was I.",
"You just set a new standard for bold content.",
"Bro my heart actually skipped. This is WILD.",
"Someone's parents are gonna see this and faint 😂",
"This is the kind of post that makes legends.",
"You got bigger balls than most guys I know.",
"Unhinged queen behavior and I'm ALL in. 💅🔥"
],
/* ══════════════════════════════════════════════════════════════════════════
HIPS (hip-focused / curves / waist posts)
══════════════════════════════════════════════════════════════════════ */
hips: [
"The pose is perfect.",
"That angle is everything.",
"This pic is so well done.",
"Those hips don't lie and neither do I. 🫠",
"Curves for days, damn.",
"That waist-to-hip ratio is illegal.",
"I'm staring and I'm not even sorry.",
"You were sculpted by the gods.",
"Every curve is in the right place.",
"This is what perfection looks like from the side.",
"Hourglass figure and she knows it ⏳",
"I suddenly believe in miracles.",
"Your body is a work of art.",
"That silhouette is absolutely deadly.",
"If hips could talk, yours would be screaming.",
"I tried to look away. I failed.",
"The way those hips sit... even in a photo. 🥵",
"Someone call an ambulance, those curves killed me.",
"Built like a fantasy.",
"That pose + those hips = game over.",
"I want to grab those hips so bad it hurts. 😩",
"Shakira is taking notes rn.",
"The ratio is INSANE.",
"How does your waist do that??? Is that photoshop??",
"It's not. She's just built different.",
"Those hips could cause car accidents.",
"I bet you can't walk through a doorway sideways 😂",
"The way that dress hugs your hips... fuck.",
"This is geometry I actually want to study.",
"Wide hips save lives. Scientific fact.",
"My hands would look perfect right there.",
"God spent extra time on those curves.",
"The hip dip? Nonexistent. Just smooth perfection.",
"How do jeans even fit over those hips?",
"She was drawn by an anime artist I swear 😂",
"That curve from waist to hip is mesmerizing.",
"I keep replaying this photo in my head ngl.",
"Hips like these should come with a health warning ⚠️",
"My jaw hurts from dropping it so hard.",
"Built like a coke bottle and twice as refreshing.",
"The SHAPE. I'm losing my mind.",
"I would draw you if I could draw.",
"Those hips + that confidence = unstoppable.",
"The sway those must have when you walk... lord.",
"Girl your hip game is unmatched 💅🔥",
"I zoomed in on the waistline and my brain crashed.",
"Nature doesn't make curves like this. She's a glitch.",
"This is the body type that starts wars historically.",
"I'd worship at the altar of those hips.",
"The curvature is beyond physics at this point.",
"POV: you discover the perfect female form.",
"I've never been so jealous of fabric in my life.",
"That tight dress on those hips is a violation.",
"I'm down horrendous and those hips are why.",
"Bro she's got the golden ratio going on fr.",
"If I was an artist I'd only paint your hips.",
"I need a chiropractor after that neck snap. Damn.",
"Wide-hip supremacy. I said what I said.",
"She's curvy in all the RIGHT places. 😮💨",
"Every outfit looks custom-made on that body.",
"The way the light catches those curves though.",
"My screen can barely contain those hips.",
"Built like a goddess from mythology.",
"I could watch you turn sideways on repeat forever.",
"Those hips are doing things to my mental health.",
"Thigh gap? Who cares. Those HIPS. 🔥",
"I wanna be reborn as the belt on those hips.",
"Honestly disrespectful to everyone around you irl.",
"That body ratio is what scientists dream of.",
"She walks into a room and everyone shuts up. Guaranteed.",
"The curve of those hips is a religious experience.",
"Baby-making hips and she knows it. 😈",
"I've been scrolling back to this for 20 minutes.",
"Those hips just grabbed my soul through the screen.",
"This is peak female form. Don't argue with me.",
"I'd let those hips ruin my credit score.",
"The width of those hips compared to that tiny waist... insane.",
"I showed this to my anatomy professor and he cried.",
"That hip bone peeking out is doing THINGS. 👀",
"I want those hips to crush my skull. Sorry. Not sorry. 💀",
"Girl how do you even find pants that fit 😂💕",
"My ex had nothing on those curves. NOTHING.",
"The way your body moves must be hypnotic irl.",
"Hips built for grabbing. I said what I said. 😏"
],
/* ══════════════════════════════════════════════════════════════════════════
ASS (butt-focused posts)
══════════════════════════════════════════════════════════════════════ */
ass: [
"This post is dangerous. 🍑",
"I had to stop scrolling.",
"You know what you are doing.",
"That ass deserves its own zip code.",
"I'm looking respectfully. Okay maybe not. 👀",
"Peach emoji doesn't do it justice.",
"That thing is a whole damn meal.",
"I can't focus on anything else now.",
"You broke my neck with this one.",
"Lord have mercy. 🙏",
"How is that even real?",
"That jiggle physics tho 💀",
"I need to sit down after this.",
"I showed this to my friends. You're welcome.",
"Certified cake. No debate. 🎂",
"The back view hits different.",
"That ass is disrespectful in the best way.",
"I just became your biggest fan. Literally.",
"You're carrying the whole feed on your back. Literally.",
"I dropped my phone again.",
"That's a WAGON. Holy shit. 🚛",
"I could bounce a quarter off that.",
"That ass just entered the chat and won.",
"I tried to think of something smart to say. Failed. Just... ass. 🍑",
"She knew EXACTLY what angle to use.",
"Thicker than a Snickers and I'm starving.",
"Okay I'm staring and I don't care who knows.",
"The way those cheeks sit... disrespectful.",
"I had to put my phone down and process this.",
"That's not a butt that's a MONUMENT.",
"My face. Right there. Please. 😩",
"Squat game must be INSANE.",
"I would follow that ass into battle.",
"The gap between those cheeks is doing things to me.",
"I'm down bad. I'm down terrible. I'm down cataclysmic.",
"That ass should have its own Instagram account.",
"I'm sorry but I can't NOT stare.",
"She turned around and the whole room went quiet.",
"This pic just ruined my productivity for the day.",
"Bro that's not an ass that's a WHOLE BAKERY 🍞",
"Whoever's behind her is living their best life.",
"I want to use that as a pillow ngl.",
"How many squats for THAT? Asking for science.",
"My brain: 'say something clever.' Also my brain: 'ass.' 🍑",
"If asses had a Hall of Fame, this is first ballot.",
"That's the kind of ass that starts fights in clubs.",
"She sat down and the chair said 'thank you' 💀",
"Built like a goddamn video game character.",
"I need to go outside and touch grass after this.",
"God really said 'here have extra' 🔥",
"The way those jeans are screaming for help 😂",
"My therapist is gonna hear about this post.",
"I'm not a simp but that ass is turning me into one.",
"I wanna bite it. Sorry. Had to say it.",
"That peach is RIPE 🍑🥵",
"I've seen a lot of asses on this app. This is #1.",
"I just understood the meaning of 'thick.' Thank you.",
"The booty so round it's creating gravity.",
"That thing could stop a war. Or start one.",
"Every pair of pants you own is a hero.",
"I can hear that ass clapping through the screen 💀😂",
"Please never stop posting back angles.",
"Her ass is a public service at this point.",
"I bet she breaks chairs on the regular 😈",
"That dump truck needs a CDL license.",
"Girl that BOOTY 🍑🔥 absolute queen!",
"I'm not even ashamed of zooming in anymore.",
"My whole day just improved by 300%.",
"Mans really scrolling through comments to see if anyone agrees... yep. We ALL see it.",
"If I could frame an ass, this would be on my wall.",
"That angle? That lighting? That ASS? Perfect trifecta.",
"She's packing more than my suitcase 💀",
"This is the ass that launched a thousand DMs.",
"I respectfully request more content like this. Please.",
"Cake cake cake cake 🎂🎂🎂",
"Bro I literally stopped eating to look at this.",
"That ass has more followers than most people's whole account.",
"I need to write a poem about this. I'm not even kidding.",
"She's out here making flat asses cry worldwide.",
"I'd sell a kidney just to... nevermind. 💀",
"That thing has gravitational pull. I'm being sucked in.",
"She posted this knowing EXACTLY what would happen.",
"I've watched this pic bounce in my imagination 100 times.",
"Girl the PEACH 🍑✨ I'm jealous AND impressed."
],
/* ══════════════════════════════════════════════════════════════════════════
BOOBS (chest / cleavage / top-focused posts)
══════════════════════════════════════════════════════════════════════ */
boobs: [
"Those are impossible to ignore.",
"I tried to read the caption. I failed. 😂",
"That top is working overtime.",
"Gravity who? She doesn't know her.",
"My eyes went straight there. No shame.",
"You are blessed and we are grateful. 🙏",
"That cleavage could stop traffic.",
"How does that shirt even hold up?",
"Double trouble and I love it.",
"Okay those are definitely real, right? 👀",
"My timeline just got significantly better.",
"Someone needs to thank your genetics.",
"I'm staring respectfully... mostly.",
"That chest is carrying more than confidence.",
"I need a cold shower after this one. 🥶",
"The way they sit is just perfect.",
"Front row seats to perfection.",
"Those could start a religion.",
"I zoomed in for... research purposes. 🔬",
"God really spent extra time on you.",
"How does she find bras that fit tho??? 😂",
"That neckline is dangerously low and I'm not complaining.",
"Motorboat incoming. Sorry. Not sorry. 💀",
"Those tits are defying every law of physics.",
"I was gonna say something clever but... boobs.",
"My face would fit perfectly right there. Just saying.",
"Spilled my coffee looking at this. Worth it.",
"That top is doing the lord's work holding those in.",
"They're so perfect it should be a crime.",
"Big tit energy. That's a thing now. 😤",
"I'm a boobs guy and this post just confirmed it.",
"I bet you can't see your feet when you look down 😂",
"The way they just SIT there. Like two perfect pillows.",
"My brain went offline the second I saw this.",
"I suddenly forgot every word I know.",
"Those buttons are holding on for dear life. Heroes.",
"Tits like these are a gift to humanity.",
"This pic just became my phone wallpaper. No shame.",
"I'm a straight woman and EVEN I can't look away 💀",
"Drop the bra brand sis because DAMN 👀",
"POV: God's favorite creation.",
"My hands are twitching. Help.",
"That titty drop would crash the internet.",
"She's stacked and she knows it. Queen shit. 👑",
"If I was a bra I'd volunteer for that job.",
"Cleavage game is UNMATCHED on this app.",
"Those boobs just made my entire week.",
"I have to ask... are they really natural???",
"Every shirt she owns is basically a push-up bra.",
"Bro I showed my homie and he's in shock rn 😂",
"I bet hugging you is like hugging heaven.",
"I need a moment of silence for that cleavage. 🫡",
"My boy motorboat Dave would lose his mind rn.",
"Them titties could feed a whole village 💀😂",
"I was scrolling fast and those made me STOP.",
"The ratio of waist to chest is absolutely nuts.",
"Two reasons to follow this account. I see them clearly.",
"I swear those get bigger every post. Not complaining.",
"If boobs could talk, those would be singing opera.",
"This is what bras were invented for. To contain greatness.",
"Respectful king here... but DAMN those are nice.",
"I choked on my drink. Literally. Because of boobs. 🥤💀",
"That low-cut top is fighting for its life.",
"I wish I could unsee this just so I could see it again for the first time.",
"Every straight man in a 5 mile radius just felt a disturbance.",
"I'm a simp but only for THIS. 🫠",
"I just realized I've been staring at this for 5 minutes.",
"Those boobs have their own gravitational field.",
"Whoever took this photo knew EXACTLY what they were framing 📸",
"Bra companies should sponsor you fr.",
"I'm filing this under 'things that make life worth living.'",
"Twin peaks looking magnificent today 🏔️🏔️",
"She's carrying two national treasures.",
"Titty game top tier. GOAT status. 🐐",
"Girl your boobs are INCREDIBLE ugh I'm jealous 😩💕",
"Every time you post I forget what I was doing.",
"This pic has more bounce potential than a trampoline park.",
"I wanna bury my face and forget the world exists.",
"Nah she has to be doing this on purpose. That angle. 📐🔥",
"That zipper is under more stress than me at work.",
"I suddenly believe in a higher power looking at these.",
"If I could only save one pic on my phone it would be this.",
"Those are heavy and I volunteer to hold them. 🙋♂️",
"I showed this to a blind guy and he said 'describe them again' 💀",
"Girl those are PERFECT. Genetics won. 💅✨"
],
/* ══════════════════════════════════════════════════════════════════════════
HOT (general hot / attractive posts)
══════════════════════════════════════════════════════════════════════ */
hot: [
"You're on fire, literally. 🔥",
"Temperature just went up in here.",
"That look is deadly.",
"Someone turn on the AC please.",
"Hot doesn't even begin to describe this.",
"You just melted my screen.",
"Is it getting warm or is it just you? 🥵",
"Absolutely smoking.",
"My feed can't handle this heat.",
"You're giving main character energy.",
"I burned my fingers scrolling past this.",
"10/10 would double tap again.",
"You're too hot for this app honestly.",
"This pic set my phone on fire. 📱🔥",
"How do you keep getting hotter every post?",
"I need water after seeing this.",
"You should come with a fire warning.",
"Hottest thing I've seen all week.",
"Forget the sun. You're the real heat source.",
"Dangerously hot. Proceed with caution. ⚠️",
"My AC bill just went up because of you.",
"Bro she is SCORCHING.",
"I literally felt heat coming off my screen.",
"How are you allowed to post things this hot?",
"Somebody spray me with a hose please.",
"This is too much for a Monday morning. 😩🔥",
"I'm filing a complaint. You're too hot.",
"She could warm up a whole city in winter.",
"If hotness was a crime you'd be serving life.",
"I think my phone actually heated up from this pic.",
"There's hot. There's smoking. Then there's THIS.",
"I need to splash water on my face brb.",
"She's a walking furnace and I want to burn.",
"Climate change? Nah it's just her posting again.",
"My thermostat just broke looking at you. 🌡️",
"You didn't have to go this hard. But you did.",
"I've seen hot. This is supernova level.",
"Every fire department in the city just got an alert.",
"I can't look at this at work someone might see my face 😳",
"POV: your screen slowly melting from the heat.",
"She makes summer look like winter. THAT hot.",
"I need an ice bath after scrolling your feed.",
"You're hotter than the surface of the sun. Literally.",
"Girl you are FIRE. Actual real life fire. 🔥🔥🔥",
"I'm not even thirsty I'm DEHYDRATED looking at this.",
"That's it. You win hottest person on the internet.",
"Every time you post, hell gets cooler by comparison.",
"I burned my thumb double-tapping this 😂",
"You should be illegal. Like seriously against the law.",
"I told myself I'd scroll past. I lied.",
"Smoking doesn't even cover it. You're an inferno.",
"I was fine until I saw this. Now I'm sweating.",
"My screen protector is melting. Thanks. 💀",
"Five alarm fire right here. 🚒🚒🚒",
"I'm calling the fire department on your whole account.",
"Even my girl said you're hot. That's saying something.",
"You raised the global temperature with one pic.",
"I've been burned. Third degree. By pixels.",
"The hottest follow on my entire feed. Bar none.",
"I was cold until I opened your profile. Problem solved. 🔥",
"How is one person this consistently hot??? Like HOW??",
"Lava is cold compared to you rn.",
"This post should come with oven mitts.",
"She doesn't need a filter. She IS the filter.",
"Okay I'm officially overheating. Thanks for that.",
"You just turned my phone into a space heater.",
"HOT. That's the whole comment. Just HOT. 🥵🔥",
"I showed this to my buddy and he forgot his girlfriend's name 😂",
"Spontaneous combustion is a real risk with your posts.",
"Absolute smokebomb. Wow.",
"I'm writing this comment from the hospital. Heat stroke. Worth it.",
"She's not just hot she's DANGEROUS.",
"You could fry an egg on this post.",
"Girl the HEAT you bring every single time 🔥💅",
"My cold heart just thawed.",
"I touched my screen and it actually felt warm. Placebo? Nah. 😂",
"This post alone is responsible for global warming.",
"She's so hot she makes the devil sweat.",
"I was eating ice cream and it melted. From this pic. True story.",
"My smoke alarm just went off. Coincidence? I think not. 🚨",
"Hot take: you're the hottest person alive.",
"I can't stop sweating since I followed this account.",
"She makes fire jealous. That's how hot she is.",
"Somebody open a window. Please. 🪟🥵"
],
/* ══════════════════════════════════════════════════════════════════════════
TEASING (suggestive / leaving-to-imagination posts)
══════════════════════════════════════════════════════════════════════ */
teasing: [
"You're such a tease and I love it. 😏",
"Show more. I dare you.",
"You know exactly what you're doing huh.",
"Stop teasing us like this, it's not fair. 😩",
"That little peek is doing things to me.",
"You left just enough to the imagination.",
"A tease done right. Respect.",
"You're playing with fire and we're all burning. 🔥",
"The suspense is killing me.",
"Give us more and stop being shy.",
"That hint of skin is driving me crazy.",
"You know how to keep us wanting more.",
"This is torture in the best possible way.",
"The way you tease should be studied.",
"One more button and I'm done for. 😵",
"You're dangling the bait and we're all biting.",
"Half-hidden and still the hottest thing here.",
"You're a master of suggestion.",
"That peek-a-boo game is undefeated.",
"I hate you for teasing. Please never stop.",
"This is worse than a cliffhanger on a TV show. 📺😤",
"She said 'look but don't touch' and I'm SUFFERING.",
"The imagination is running wild right now.",
"You revealed just enough to ruin my whole day.",
"I see what you did there. And I want MORE.",
"The tease is the whole point and she KNOWS it.",
"I can see just enough to know I'm fucked. 💀",
"My mind is filling in the blanks and it's NOT PG.",
"That barely-there situation is driving me INSANE.",
"You pulled the curtain back juuuust enough. Evil genius.",
"Nah she's torturing us on purpose at this point.",
"The art of almost-showing is your superpower.",
"This is edging but for Instagram. 😩",
"I need to see more. Please. I'm begging.",
"She knows we're all zooming in. And she likes it.",
"The gap between what I see and what I imagine is killing me.",
"One more inch lower and the internet explodes.",
"I've never wanted fabric to disappear so badly.",
"Girl you KNOW what you're doing to us rn 😤",
"The subtle skin flash... devastating.",
"You're leaving crumbs and I'm starving for the whole cake.",
"I can almost see it and 'almost' is the cruelest word.",
"Teasing level: expert. I'm losing my mind.",
"The way you hide juuust enough... criminal.",
"She's showing 10% and still hotter than 100% of everyone else.",
"This is psychological warfare and I surrender.",
"Bro she's weaponizing subtlety 😂🔥",
"My brain: 'scroll past.' My thumb: 'nah we're staying.'",
"You turned 'barely showing anything' into an art form.",
"I zoomed in and got NOTHING. You're evil. 😈",
"The way that strap is sliding down... is it intentional?? 👀",
"I need the uncensored version pls. For science.",
"She said 'use your imagination' and my imagination said 'GLADLY.'",
"I hate this. I love this. I hate that I love this.",
"If teasing was an Olympic sport you'd have gold. 🥇",
"You're giving us appetizers when we need the full meal.",
"I've been staring at this trying to see more. 10 minutes. Help.",
"She KNOWS we're all dying and she's enjoying it.",
"The angle is ALMOST there and that's what makes it perfect.",
"Cruel. Unusual. Punishment. Also please post more. 🥵",
"That little sliver of skin is doing more than a full nude ever could.",
"You understand the power of mystery and it's terrifying.",
"This post is a promise and I'm WAITING for delivery.",
"Half-dressed > fully dressed. And you just proved it.",
"I need therapy after this level of teasing.",
"She's got the entire comment section on a leash 😂",
"My imagination just ran a whole marathon because of this.",
"Peeking > showing. You understand the assignment.",
"The things I see in my head rn... you did this.",
"This is controlled cruelty and I'm the victim.",
"Every inch you DON'T show makes me want it more.",
"She posted a tease at 11pm and now I can't sleep. Thanks. 💀",
"This is the content that keeps me up at night. Literally.",
"You could teach a masterclass in teasing. I'd pay full price.",
"Bestie you are EVIL for this 😩🔥 and I love you for it.",
"Almost-nudity > nudity. You cracked the code.",
"The way that towel is hanging... one gust of wind away from chaos. 🌬️",
"She said 'I'll show you... later' and I'm still waiting.",
"The peek of the underwear strap... calculated genius.",
"My blood pressure spikes every time you post a tease.",
"You could sell tickets to this tease show fr.",
"I'm refreshing the page hoping for more. Pathetic? Yes. Stopping? No.",
"She treats her followers like cats chasing a laser pointer 😂",
"Girl you are a menace with these teases 💅😈"
]
}>>
/* ============================================================================
FOTOGRAM DM DATA Heat-based reactive system (v2)
Each anon message has a choices array → options for that message.
Options are short, natural, lowercase, no prefix.
delta: heat change (multiplied by heatMult)
triggersPhoto: true → photo submenu opens
isRejection: true → persistence decreases
v2 CHANGELOG:
- react_positive / react_negative / react_neutral added to each persona.
When player picks a choice, anon reacts from these pools FIRST, then
sends heat-based new message.
- Small pools expanded (2-3 → 4-6 entries).
- close pools expanded.
============================================================================ */
/* ── PERSONA CONFIG ──────────────────────────────────────────────────── */
<<run setup.fdmPersonas = {
naive: { startHeat:15, heatMult:0.8, persistence:2, maxTurns:10, boredHeat:30, boredTurn:7, gender:"male" },
lustful: { startHeat:35, heatMult:1.2, persistence:4, maxTurns:14, boredHeat:35, boredTurn:9, gender:"male" },
pervy: { startHeat:55, heatMult:1.5, persistence:5, maxTurns:16, boredHeat:40, boredTurn:10, gender:"male" },
flirty: { startHeat:20, heatMult:1.0, persistence:3, maxTurns:12, boredHeat:30, boredTurn:8, gender:"female" },
wild: { startHeat:35, heatMult:1.2, persistence:4, maxTurns:14, boredHeat:35, boredTurn:9, gender:"female" }
}>>
/* ── RUNTIME TEXT POOLS (EXPLICIT TAG / FOTO TAG) vulgar/explicit ───── */
<<run setup.fdmRuntimeTextPools = {
naive: {
explicit: [
"I'd like to see something a bit more personal",
"I bet you have a bolder side",
"send me your tits or something, that'd be great",
"Something a bit bolder would be nice",
"I'm curious what else you've got"
],
photo_send: [
"I'll send a photo",
"okay, sending one",
"here, sending something",
"sending you one",
"here you go"
]
},
lustful: {
explicit: [
"I'm hard just thinking about you. Send something.",
"I want to see your pussy and ass. Don't make me wait.",
"Your tits got me going. Now show me the rest.",
"Send something hotter. I want to see every inch of you.",
"You're so hot. One more show me your pussy.",
"I need to see you. Send. Now.",
"Your turn. Pussy, tits, ass send whatever."
],
photo_send: [
"Sending you one now. Your turn.",
"Here. Look what you do to me.",
"Sent. Now you.",
"There. Your go.",
"Done. Now you send."
]
},
pervy: {
explicit: [
"I'm rock hard for you. Send your pussy.",
"My cock's ready. Your tits and ass. Send.",
"Exactly what I wanted. More. Now.",
"I need to see your pussy. Send it.",
"I'm so hard. Your turn. Don't tease.",
"Send. I want everything. Don't hold back.",
"Your pussy. Now. Send."
],
photo_send: [
"There. For you. Now you send.",
"Sending. Your go.",
"Done. Your turn.",
"That's for you. Your move.",
"Sent. You're up."
]
},
flirty: {
explicit: [
"You look incredible. Send another :)",
"Don't stop. One more :)",
"You're killing me. Send more :)",
"So hot. One more please :)",
"I need more of you :)"
],
photo_send: [
"Sending you something :)",
"Here's one for you :)",
"Your turn :)",
"This one's for you :)",
"Sent :) You're up :)"
]
},
wild: {
explicit: [
"You're so hot. Send one more :)",
"Keep going. I want more :)",
"Wanna take this to WhatsApp? I need more :)",
"Don't stop. More please :)",
"You're so good. Send another :)"
],
photo_send: [
"Sending :) You'll like this :)",
"Here you go :)",
"One for you :)",
"Sent :) Your go :)",
"There you go :)"
]
},
generic: {
explicit: [
"wasn't expecting that",
"that got really direct",
"this is moving a bit fast"
],
photo_send: [
"I'll send a photo",
"okay, sending one",
"here, sending something"
]
}
}>>
/* ── MESSAGE TEXT POOLS (2–4 variants per slot; use [POOL:key]) ── */
<<run setup.fdmMessagePools = {
/* NAIVE */
naive_w1: ["Do you have any photos besides what's on your page? Just curious.", "Anything besides what's on your feed?", "Got other photos besides what you've shared?", "Do you keep any photos that aren't on your page?", "Anything else you don't post publicly?"],
naive_w2: ["It'd be great if you sent one more photo.", "Could you send one more? I'd love that.", "One more would be awesome.", "I'd love another one if you're up for it.", "One more photo would make my day."],
naive_w3: ["I really liked that style, do you have more like that?", "Loved that style, got any more?", "Do you have more photos like that?", "Got more of that kind?", "Any similar ones?", "That kind of shot was really nice. Any others?", "Same vibe again would be great."],
naive_w4: ["Your page photos are great, I think you could be bolder.", "Photos are nice, you could be a bit bolder I think.", "Your feed's nice but you could send bolder stuff too.", "You've got a good eye I bet you could go a bit bolder too.", "Lovely photos. Would love to see something a bit more daring sometime."],
naive_h1: ["Would you consider sending a photo? I'm really curious.", "Could you send a photo? So curious.", "Would you send one photo?", "I'd love to see a photo from you if you're okay with it.", "One photo would mean a lot. No pressure though."],
naive_h2: ["Just one, wanted to see how you're doing.", "Just send one, I want to see how you are.", "One would be enough, curious how you're doing.", "A single photo would be nice. Just to see you.", "One pic is enough. I'm just curious."],
naive_h3: ["Could you send something a bit more personal? Honestly curious.", "Would you send something more personal? Curious.", "Could you send something bolder?", "Something a bit more personal would be amazing.", "I'd love something a little bolder if you're comfortable."],
naive_h4: ["I'd love a photo from you but I won't push.", "I'd really like you to send a photo, no pressure.", "I'd love one from you, but no pressure.", "A photo would be great whenever you're ready. No rush.", "I won't push but I'd really like one when you feel like it."],
naive_react_sexy: ["You look amazing, honestly. Thank you. 😍", "That was really nice. Thanks. 🔥", "So pretty. I'm glad you sent that.", "You look great. Really appreciate it. 💕", "That was lovely. Thank you.", "Wow. You're so pretty. Thanks 😊"],
naive_react_nude: ["Whoa. You're gorgeous. Thanks for trusting me with that. 😳💕", "I wasn't expecting that. In a good way. Thanks. 🔥", "That's… really hot. Thank you.", "You're beautiful. Thanks for sharing that with me. 😍", "That meant a lot. You're stunning.", "Damn. You're incredible. Thank you. 👀"],
/* LUSTFUL explicit/vulgar */
lustful_ex1: ["There's me. Your turn show me something. Pussy, tits, whatever you're comfortable with.", "I sent you a pic. Fair's fair send me something back. I'm not picky.", "You've seen what I'm working with. Now I want to see you.", "That's me. Your move. Send me something back.", "I showed you. Now I want to see you. Your call what you send."],
lustful_ex2: ["So? What do you think? Honest answer.", "Did that do anything for you? Tell me.", "I need to know did you like it?", "Your honest opinion?", "Did you like what you saw?"],
lustful_ex3: ["Don't stop now. One more I want to see all of you.", "Keep going. I'm hard and I want more.", "Your turn. Tits, ass, pussy I'll take whatever you send.", "One more. I want everything. Don't hold back.", "Again. I'm not done looking. Send more."],
lustful_ex4: ["Did it get you wet? Be honest.", "So did that work for you or what?", "Tell me you liked it.", "Did that do it for you?", "Your turn to be honest with me."],
lustful_w1: ["Your turn. I showed you mine show me yours. Pussy, tits, ass, I'm not fussy.", "Fair swap? You send something back. Whatever you're in the mood to share.", "I sent. Now you. Don't make me beg.", "I put it out there. Your turn. I'll take anything you send.", "You've seen mine. Now show me yours. Deal?"],
lustful_w2: ["You've seen my dick. Your move.", "I put mine out there. Your turn to return the favour.", "I sent. Now you. Come on.", "That was me. Your go.", "I showed you. Fair's fair you send something."],
lustful_w3: ["I want to see you. All of you. Send me something.", "Your turn to show off. I'm waiting.", "I've shown you what I've got. Now I want to see you tits, ass, whatever.", "I need to see you. Send something. Whatever you're comfortable with.", "Your turn. I want to see your body. Don't make me wait."],
lustful_h1: ["Send something. I'm hard and I need to see you. Pussy, tits just one.", "Your turn. I want to see your body. Don't keep me waiting.", "Come on. Send me something. I'm dying to see you.", "One pic. I need to see you. Your call what you send.", "Send me something. I'm ready for it."],
lustful_h2: ["That's me. Now you. Send something back.", "There you go that's what you do to me. Your turn.", "You've seen it. Now show me yours.", "That's what I'm working with. Your move.", "I sent. Your turn. Don't leave me hanging."],
lustful_h3: ["One more. I need more. Pussy, tits, ass send whatever.", "Don't stop. Send another one. I'm ready for it.", "Again. I'm still hard. Send more.", "More. I want another. Send.", "One more pic. I'm not done yet."],
/* PERVY explicit/vulgar */
pervy_w1: ["I sent you my cock. You know the drill you send something back. Pussy, tits, ass.", "Swap. I showed you mine. Now you show me yours.", "Dick pic delivered. Your turn. Don't leave me hanging.", "That was me. Your turn. Pussy, tits, ass send something.", "I sent. You're up. Don't tease."],
pervy_w2: ["I sent. You're up. Send something.", "Your turn. I showed you now you show me.", "I sent my dick. Where's yours? I mean you know what I mean. Send something.", "My turn's done. Yours. Send.", "I put it out there. Now you."],
pervy_w3: ["That one was just for you. Hope you're enjoying the view.", "Special delivery for your eyes only.", "Took that thinking about you. Your turn.", "That was for you. Like it? Your go.", "Just for you. Now send something back."],
pervy_h1: ["You can do better than that. Send something hotter. Pussy. Ass. Your turn.", "Level up. I want to see your pussy and ass. Send.", "More. I want more. Show me everything.", "Step it up. I want pussy and ass. Send.", "That was weak. Send something that really shows off."],
pervy_h2: ["Something bolder. I'm already hard give me something to really get off to.", "Send one that shows everything. I want to see your tits and ass.", "Bolder. I want to see what you're hiding.", "I need something hotter. Full view. Send.", "Give me something I can actually use. Tits, ass, pussy."],
/* PERVY – photo opener (sends a photo first, upfront) – vulgar/explicit */
pervy_opener_vulgar: [
"Hey slut, look what I've got here for you.",
"Thought of you and sent it. Your turn.",
"That's what you do to me. See? Now send yours.",
"Slut, I sent this thinking about what I'd do to you. Go on.",
"Look at that. That's for you. I'm ready. Your turn.",
"Hey, this is your fault. Now you send.",
"Thinking about your mouth on this. Keep going.",
"There it is. That's for your pussy. Send already."
],
/* FLIRTY */
flirty_w1: ["Your page has such a nice vibe. Do you shoot your own photos?", "I really like your style. Do you plan your shots or just go with the moment?", "Your profile feels very genuine. I like that :)", "Your feed has such a good energy. Do you take your own pics?", "I like how your page feels. Very you :)"],
flirty_w2: ["Here, this one's for you :) Your turn now.", "I went first :) Now you.", "Sent you mine :) Fair's fair, your turn.", "This one's for you :) You're up :)", "I sent one :) Your go :)"],
flirty_w3: ["That one felt really personal, in a good way :)", "I liked that photo a lot, very natural :)", "You have a good eye, that shot was really nice :)", "That photo was so nice. Very you :)", "I really liked that one. Good vibe :)"],
flirty_h1: ["If you're up for it, send one cute photo :)", "Your turn if you feel like it :)", "Only if you're comfortable - I'd like one from you :)", "I'd love one from you when you're ready :)", "Your turn - only if you want to :)"],
flirty_h2: ["We can keep chatting too, no pressure :)", "If you want to keep the vibe going, send one more :)", "Your call - we can chat or swap one more :)", "No pressure - chat or one more pic, whatever you prefer :)", "We can take it slow or swap one more. You choose :)"],
flirty_react_sexy: ["God, you're hot. I love it :) 🔥", "You look incredible. Seriously :) 😍", "So sexy. Keep them coming :) 💋", "You're so good-looking :)", "That was hot. More please :) ❤️🔥", "Mmm you're so fine :) 👀"],
flirty_react_nude: ["You're gorgeous. Thank you :) 😍💕", "Your body is amazing. Thanks for sharing :) 🔥", "So hot. I'm glad you sent that :) 💋", "You're stunning. Thanks for trusting me :) ❤️🔥", "That was beautiful. Thank you :) 👀", "Fuck you're sexy. More please :) 😈"],
/* WILD explicit/vulgar */
wild_ex1: ["You have a bold vibe. I like it :)", "You're fun to talk to. I can tell :)", "Not gonna lie, you caught my attention :)", "Your energy is bold. I'm here for it :)", "You seem like fun. I like that :)"],
wild_ex2: ["Want to keep this playful? I'm in :)", "Your energy is good. I like where this is going :)", "We can keep it spicy or keep it chill, your call :)", "I'm down for whatever vibe you want :)", "Playful or chill - you lead :)"],
wild_ex3: ["So what are you like off-camera? :)", "You seem confident. Is that always your vibe? :)", "What's your style when you're not posting? :)", "You give off confident vibes. Is that the real you? :)", "What are you like when you're not on your feed? :)"],
wild_w1: ["If you're into it, we can do a fair photo swap :)", "We can trade one each if you want :)", "Only if you're comfortable - I can send one, then you :)", "Fair swap? I send one, you send one :)", "Up for a trade? One each :)"],
wild_w2: ["I showed you mine. Now it's your turn :)", "Sent you one. Fair's fair :)", "I went first. Your turn now :)", "That was me :) You're up :)", "I sent. Your go :)"],
wild_w3: ["Want to keep this teasing a little? :)", "If you're in the mood, we can go a bit bolder :)", "Your call - chat more, or send one :)", "We can keep teasing or you send one. Your choice :)", "Chat more or send a pic - I'm easy :)"],
wild_h1: ["If you're in the mood, send one photo :)", "One more if you want - no pressure :)", "Your turn if you're comfortable :)", "I'd love one from you when you're ready :)", "Your go - only if you feel like it :)"],
wild_h2: ["I'm curious, but no stress - only if you want :)", "Could send one more if you're feeling it :)", "We can keep chatting too, no pressure :)", "No pressure. One more pic or just chat - you choose :)", "Your call. One more or we keep talking :)"],
wild_react_normal: ["Nice :) I like your vibe. 🔥", "That was cute :)", "I liked that one :)", "Good one :) 💕", "You're cute :)"],
wild_react_open: ["Ohh, that was good :) 😏", "Mmm, I liked that :) 🔥", "That's a good look on you :)", "That hit different :) 💋", "I liked that a lot :) 👀"],
wild_react_sexy: ["You're really hot :) 🔥😍", "Okay wow, that looked amazing :) 💕", "You definitely know your angles :) 😈", "So hot :) More. 👀", "You look amazing :) ❤️🔥", "Fuck you're sexy :) 💦"],
wild_react_nude: ["You're stunning :) 😍🔥", "That was bold, I liked it :) 💋", "You look incredible :) ❤️🔥", "So good :) I want more. 👀", "You're gorgeous :) 😈", "Damn. Send more :) 💦"]
}>>
/* ── AFTER COCK OPENER REACTION (double message: this first, then "your turn send") ── */
<<run setup.fdmAfterCockOpenerReaction = {
pervy: [
"That's what you do to me. Took one look at your profile and boom.",
"Hard for you. Think about what I could do with that.",
"See what I'm packing? All yours if you play nice.",
"That's me when I think about you. Your turn to show me something.",
"Rock hard. You did that. Now show me what I'm missing.",
"There it is. Want to see it in person? Send something first.",
"Couldn't help it. You got me going. Your move.",
"That's what thinking about your pussy does to me.",
"You did that to me. Now show me what you've got.",
"All for you. Your turn to send."
],
lustful: [
"That's what you do to me. Couldn't help it.",
"I'm hard. You did that. Now I want to see you.",
"See? That's all for you. Your turn.",
"Got a little carried away thinking about you. Send me something back.",
"There you go. That's me when I imagine you. Fair's fair show me yours.",
"You saw it. What do you think? Now send something.",
"Thinking about your tits and pussy got me like this. Don't leave me hanging.",
"I want to see your ass. Send me something.",
"That's me when I think about you. Your go.",
"Fair swap. I sent. Now you."
],
naive: [
"Sorry, I got a bit carried away. That's what you do to me.",
"That just… happened when I thought about you. Oops?",
"You can tell I'm into you, right?",
"I couldn't help it. You're really pretty.",
"That was a bit forward. But you're worth it."
],
flirty: [
"That's for you. Like what you see? :)",
"I got a little excited. Your fault :)",
"So? Do I get something back? :)",
"Your turn :) I showed you mine :)",
"That's what you do to me :) Send me something :)"
],
wild: [
"That's what I'm working with. Your turn :)",
"Got a bit excited. Send me something back :)",
"Like it? Show me what you've got :)",
"Fair's fair :) I sent. You send :)",
"That's for you :) Your go :)"
],
generic: [
"There. Your turn.",
"See what I mean?",
"Your move."
]
}>>
/* Anon reply to "What did you send?" says what they sent, your turn */
<<run setup.fdmWhatDidYouSendReply = {
naive: ["I sent a photo of me. Your turn I'd love to see one from you.", "Just something of me. Could you send one back?", "A little something. Now you :)", "A photo of me. I'd love one from you too.", "Something personal. Your turn when you're ready."],
lustful: ["That was my dick. Your turn show me something. Pussy, tits, whatever.", "I sent you a pic of what I've got. Now I want to see you.", "Dick pic, delivered. Fair's fair you send something back.", "That was me. Your go. Send something back.", "I showed you. Now show me yours."],
pervy: ["I sent you my cock. You know what I want in return.", "That was me. Now you. Send something.", "I showed you mine. Your move.", "My dick. Your turn. Don't keep me waiting.", "That was my cock. Send something back."],
flirty: ["I sent you something :) Your turn to show me :)", "That was me. Now you :)", "I sent. Your go :)", "A little something for you :) Your turn :)", "I showed you mine :) You show me yours :)"],
wild: ["I sent you a little something :) Your turn :)", "That was for you. Now send me one :)", "I showed you. You show me :)", "That was me :) Your go :)", "I sent one :) Fair's fair :)"],
generic: ["I sent a photo. Your turn.", "Something of me. You send one back.", "A photo. Your move."]
}>>
/* Anon reply to "What did you send?" when there was no actual attachment in last anon message */
<<run setup.fdmWhatCouldISendReply = {
naive: ["I didn't send one yet. I can send one if you want.", "Nothing yet, just words for now :)"],
lustful: ["Nothing yet. I can send one if you want.", "Haven't sent one yet. Want me to?"],
pervy: ["Didn't send yet. Want me to send one?", "No pic yet. Say the word."],
flirty: ["I haven't sent one yet :) I can if you want.", "No photo yet, just teasing :)"],
wild: ["Didn't send yet :) I can send one if you want.", "No pic yet. Want one? :)"],
generic: ["I didn't send one yet.", "No photo yet."]
}>>
/* Anon reply to rejecting question like "Why should I send?" flirty/seductive */
<<run setup.fdmWhySendReply = {
naive: ["Because I'd love to see you. No pressure, but it would make my day.", "I'm just really curious what you look like. Please?", "It would mean a lot. I promise I'll appreciate it.", "I'd really like to see you. No pressure.", "Just one. It would make me happy."],
lustful: ["Because I need to see you. I'm hard just thinking about it. Send one.", "So I can get off to you. Is that honest enough? Send.", "You'd make me so happy. And I'd return the favour. Send.", "I'm dying to see you. One pic. Send.", "Because I want to see every inch of you. Fair's fair.", "I'm hard and I need to see your body. Send.", "I want to see your pussy and tits. One pic. Send."],
pervy: ["So I can get off to you. Simple as that. Send.", "Because I'm dying to see you. One pic. That's all.", "You'd make me cum. That's why. Send.", "I need to see your pussy. One pic. Send.", "So I can use it. Send.", "So I can jerk off to you. Send.", "I want to cum to your pussy. Send it."],
flirty: ["Because I want to see you :) It would make me really happy :)", "I'm curious. And I'll send you more if you do :)", "So we can keep this going :) Send one :)", "I'd love to see you :) Just one :)", "So we can keep the vibe :) Your turn :)"],
wild: ["Because I want to see you :) One pic. I'll make it worth your while :)", "So I can see what I'm missing :) Send.", "You'd make my night. Send one :)", "I'm curious about you :) One pic :)", "So we can keep playing :) Send one :)"],
generic: ["I'd really like to see you. Please?", "Just one. I'll appreciate it.", "It would make my day.", "Because I want to see you. One photo."]
}>>
/* Anon reply when player says "I already sent. What more do you want?" (player has sent at least one photo) */
<<run setup.fdmAlreadySentReply = {
naive: ["One more like that, if you can.", "Something a bit bolder? Or we can leave it.", "Just one more. Then I'm good.", "Same kind again? Then I'm happy.", "One more when you're ready. No rush."],
lustful: ["One more like the last one. Then we're even.", "Something bolder. One more.", "Okay, one more and I'll stop asking.", "One more. Same level. Then I'm good.", "Just one more like that. Deal?"],
pervy: ["One more. Same vibe.", "One more like that. Go on.", "One more and I'm good.", "Same again. One more.", "One more. Don't stop.", "Send another. I'm not done yet.", "One more. I need to get off again."],
flirty: ["One more like that :) Then we're good :)", "Something like the last one again :)", "Just one more :)", "Same vibe again? One more :)", "One more when you feel like it :)"],
wild: ["One more like that :)", "Send one more like the last :)", "Same again, one more :)", "One more of that :)", "Again, same style :)"],
generic: ["One more like that.", "Just one more. Then I'm good.", "Same again. One more."]
}>>
<<set setup.fdmUseLegacyReactionPools = true>>
<<set setup.fdmExplicitRenderMode = "full">>
/* Player texts: persona-based pools (more vulgar for pervy/lustful, softer for naive, flirty for flirty/wild) */
<<run setup.fdmPlayerTexts = {
naive: {
photoSend: ["okay, sending one", "here, look", "sending one", "here you go", "there you go", "one sec"],
giveNumber: ["Sure, here's my number", "Here you go", "Okay, here it is", "Here's my number", "Sent you my number"],
rejectPhoto: ["no, not needed", "I'd rather not", "maybe another time", "not right now", "I'll pass"],
noPhotoAvailable: ["I don't have a new one in this style.", "Don't have one like that right now.", "Nothing new in that style at the moment."]
},
lustful: {
photoSend: ["here you go", "sending one", "there. your turn", "sent. now you", "look at that", "for you"],
giveNumber: ["Sure, here's my number", "Here. Text me.", "Sent. Hit me up.", "There you go. Call me.", "My number. Use it."],
rejectPhoto: ["nah not doing it", "no. not sending that", "pass", "not happening", "nope"],
noPhotoAvailable: ["Don't have one like that right now.", "Nothing new in that style.", "I'm out of that kind for now."]
},
pervy: {
photoSend: ["there. enjoy", "sent. your turn slut", "here. now you", "that's for you. send yours", "look what you get. send back", "done. your move"],
giveNumber: ["Here. Don't waste it.", "My number. Use it.", "Sent. Text me when you want it.", "There. Call me.", "Number's yours. Don't ghost."],
rejectPhoto: ["fuck no", "nah fuck that", "no way", "not sending you shit", "pass. next"],
noPhotoAvailable: ["Don't have one. Maybe later.", "Nothing in that style right now.", "Out of that. Send something else."]
},
flirty: {
photoSend: ["here you go :)", "sending one :)", "there :) your turn", "for you :)", "sent :) you're up :)"],
giveNumber: ["Sure, here's my number :)", "Here you go :)", "Sent :) text me :)", "My number :) use it :)", "There :) call me :)"],
rejectPhoto: ["not this time :)", "maybe later :)", "I'll pass for now :)", "no thanks :)", "not right now :)"],
noPhotoAvailable: ["I don't have one in that style right now :)", "Nothing new like that at the moment :)", "Out of that style for now :)"]
},
wild: {
photoSend: ["here :)", "sent :) your turn", "there you go :)", "for you :) enjoy", "done :) you're up :)"],
giveNumber: ["Here's my number :)", "Sent :) hit me up :)", "My number :) don't be shy :)", "There :) use it :)", "Number's yours :)"],
rejectPhoto: ["nah :)", "not now :)", "pass :)", "maybe later :)", "no thanks :)"],
noPhotoAvailable: ["Don't have one in that style :)", "Nothing like that right now :)", "Out of that for now :)"]
},
generic: {
photoSend: ["okay, sending one", "here you go", "sending one", "there you go", "here, look"],
giveNumber: ["Sure, here's my number", "Here you go", "Here's my number", "Sent you my number"],
rejectPhoto: ["no, not needed", "I'll pass", "not right now", "maybe another time"],
noPhotoAvailable: ["I don't have a new one in this style.", "Don't have one like that right now."]
}
}>>
/* Legacy: single pool fallback if fdmPlayerTexts not used */
<<run setup.fdmPlayerPhotoSendTexts = (setup.fdmPlayerTexts && setup.fdmPlayerTexts.generic && setup.fdmPlayerTexts.generic.photoSend) ? setup.fdmPlayerTexts.generic.photoSend : ["okay, sending one", "here you go", "sending one", "here, look", "there you go"]>>
<<run setup.fdmPlayerGiveNumberTexts = (setup.fdmPlayerTexts && setup.fdmPlayerTexts.generic && setup.fdmPlayerTexts.generic.giveNumber) ? setup.fdmPlayerTexts.generic.giveNumber : ["Sure, here's my number :)", "Here you go", "Here's my number", "Sent you my number"]>>
/* Player photo/video pool for interactive DM: setup.fdmPlayerPhotos in variablesPhonePhotos.twee */
/* ── FALLBACK CHOICE POOLS ───────────────────────────────────────────── */
<<run setup.fdmChoicePools = {
generic: {
default: [
{ text:"Okay", delta: 3 },
{ text:"I don't know", delta:-2 },
{ text:"What do you mean?", delta: 0 }
]
},
photo_request: {
default: [
{ text:"Why do you want one?", delta:-3 },
{ text:"Wait, slow down", delta:-5 },
{ text:"No need", delta:-8, isRejection:true }
]
},
number_request: {
default: [
{ text:"No", delta:-15, isRejection:true },
{ text:"Why?", delta:-5 },
{ text:"I'll think about it", delta: 0 }
]
}
}>>
/* ── ANON MESSAGE POOLS ──────────────────────────────────────────────── */
<<run setup.fdmMsgPools = {
/* ================================================================
NAIVE
================================================================ */
naive: {
opener: [
{
text:"I've been following your page, wanted to say hi.",
type:"opener",
choices:[
{ text:"thanks", delta: 3 },
{ text:"who are you", delta:-2 },
{ text:"how long have you been following", delta: 5 }
]
},
{
text:"I've been looking for a while, finally messaged.",
type:"opener",
choices:[
{ text:"why did you message", delta: 3 },
{ text:"okay", delta: 2 },
{ text:"what were you looking at", delta: 5 }
]
},
{
text:"Your content was really nice, wanted to say congrats.",
type:"opener",
choices:[
{ text:"thanks", delta: 4 },
{ text:"which content do you mean", delta: 6 },
{ text:"didn't have to but okay", delta: 2 }
]
}
],
/* v2: REACTION POOLS */
react_positive: [
{ text:"haha nice" },
{ text:"glad to hear that" },
{ text:"you're so sweet" },
{ text:"I liked that" },
{ text:"aww thanks" }
],
react_negative: [
{ text:"okay okay" },
{ text:"I get it" },
{ text:"don't be mad" },
{ text:"sorry, came out wrong" },
{ text:"my bad" }
],
react_neutral: [
{ text:"hmm" },
{ text:"got it" },
{ text:"okay" },
{ text:"hm okay" }
],
cold: [
{
text:"Haha at least you replied. How long have you been posting?",
type:"chat",
firstReplyOnly: true,
choices:[
{ text:"A while", delta: 3 },
{ text:"Why do you ask?", delta: 2 },
{ text:"None of your business", delta:-8 }
]
},
{
text:"Where did you take that photo? The lighting was really good.",
type:"chat",
choices:[
{ text:"Just somewhere random", delta: 4 },
{ text:"Why do you want to know?", delta: 3 },
{ text:"At home", delta: 5 }
]
},
{
text:"Just a follower, wanted to say hi.",
type:"chat",
firstReplyOnly: true,
choices:[
{ text:"Thanks", delta: 3 },
{ text:"Okay", delta: 2 },
{ text:"Why did you message?", delta: 4 }
]
},
{
text:"Name doesn't matter, your page caught my eye.",
type:"chat",
firstReplyOnly: true,
choices:[
{ text:"Thanks", delta: 4 },
{ text:"What did you like?", delta: 5 },
{ text:"Hmm", delta: 2 }
]
},
{
text:"One of your followers, hi.",
type:"chat",
firstReplyOnly: true,
choices:[
{ text:"Hi", delta: 3 },
{ text:"Hello", delta: 3 },
{ text:"What do you want?", delta: 2 }
]
},
{
text:"Seriously, your content is really quality.",
type:"chat",
choices:[
{ text:"Appreciate it", delta: 4 },
{ text:"Thanks", delta: 3 },
{ text:"Don't flatter me too much", delta: 2 }
]
},
{
text:"How long have you been into photography?",
type:"chat",
choices:[
{ text:"Pretty new", delta: 3 },
{ text:"Not photography, just sharing", delta: 2 },
{ text:"Why do you want to know?", delta: 1 }
]
},
{
text:"I like your page, you have a really genuine vibe.",
type:"chat",
choices:[
{ text:"Thanks", delta: 4 },
{ text:"Thanks, you think?", delta: 3 },
{ text:"Okay", delta: 1 }
]
},
{
text:"There's something about your posts, not everyone can do that.",
type:"chat",
choices:[
{ text:"Like what?", delta: 5 },
{ text:"Thanks", delta: 3 },
{ text:"Whatever", delta: 1 }
]
},
{
text:"My friends look at your page too, we talk about it.",
type:"chat",
choices:[
{ text:"What do you talk about?", delta: 6 },
{ text:"Interesting", delta: 3 },
{ text:"That bothers me", delta:-5 }
]
},
{
text:"Have you ever had professional photos taken?",
type:"chat",
choices:[
{ text:"No", delta: 2 },
{ text:"Why do you ask?", delta: 3 },
{ text:"Yes, once", delta: 4 }
]
},
{
text:"The last photo on your page was really nice, I liked it.",
type:"chat",
choices:[
{ text:"Which one do you mean?", delta: 5 },
{ text:"Thanks", delta: 3 },
{ text:"Hmm", delta: 1 }
]
},
{
text:"Which one do you think is your best photo?",
type:"chat",
choices:[
{ text:"I don't know", delta: 2 },
{ text:"Why do you want to know?", delta: 3 },
{ text:"I like them all", delta: 4 }
]
},
{
text:"I think you should post more often, everyone likes it.",
type:"chat",
choices:[
{ text:"I'll think about it", delta: 4 },
{ text:"I don't have time", delta: 2 },
{ text:"Thanks but no need", delta: 1 }
]
}
],
warm: [
{
text:"[POOL:naive_w1]",
type:"photo_hint",
choices:[
{ text:"Why do you want to know?", delta:-3 },
{ text:"Maybe yes maybe no", delta: 5 },
{ text:"What kind of stuff do you want to see?", delta: 8 }
]
},
{
text:"[POOL:naive_w2]",
type:"photo_hint",
choices:[
{ text:"What kind of photo do you want?", delta: 8 },
{ text:"No need", delta:-10, isRejection:true },
{ text:"Why do you want one?", delta:-2 }
]
},
{
text:"[POOL:naive_w3]",
type:"photo_hint",
choices:[
{ text:"I have some but don't share", delta:-5 },
{ text:"What kind of stuff do you want?", delta: 7 },
{ text:"It's already on my page", delta: 3 }
]
},
{
text:"[POOL:naive_w4]",
type:"photo_hint",
choices:[
{ text:"What do you mean bolder?", delta: 5 },
{ text:"This is fine", delta:-3 },
{ text:"How do you mean?", delta: 4 }
]
}
],
hot: [
{
text:"[POOL:naive_h1]",
type:"photo_request",
choices:[
{ text:"What kind of photo do you want?", delta: 8 },
{ text:"No", delta:-15, isRejection:true },
{ text:"Why should I send?", delta:-3 }
]
},
{
text:"[POOL:naive_h2]",
type:"photo_request",
choices:[
{ text:"Okay, I'll send one", delta:10, triggersPhoto:true },
{ text:"No way", delta:-12, isRejection:true },
{ text:"You don't ask for a photo by saying how are you", delta:-5 }
]
},
{
text:"[POOL:naive_h3]",
type:"photo_request",
choices:[
{ text:"Wait a second", delta:-3 },
{ text:"What do you want exactly?", delta: 5 },
{ text:"No, that's enough", delta:-15, isRejection:true }
]
},
{
text:"[POOL:naive_h4]",
type:"photo_request",
choices:[
{ text:"What kind of photo?", delta: 7 },
{ text:"No need", delta:-10, isRejection:true },
{ text:"Why?", delta:-2 }
]
}
],
fire: [
{
text:"Can we talk outside Fotogram? Could I get your number?",
type:"number_request",
choices:[
{ text:"Why?", delta:-5 },
{ text:"No", delta:-15, isRejection:true },
{ text:"Can't we just talk here?", delta:-3 }
]
},
{
text:"It's hard to keep going here, do you have WhatsApp?",
type:"number_request",
choices:[
{ text:"No", delta:-10, isRejection:true },
{ text:"I have it but I'm not giving it", delta:-8, isRejection:true },
{ text:"Why can't we stay here?", delta:-3 }
]
}
],
number: [
{
text:"I want to talk to you outside Fotogram, could I get your number?",
type:"number_request",
choices:[
{ text:"No", delta:-15, isRejection:true },
{ text:"Why?", delta:-5 },
{ text:"I'll think about it", delta: 0 }
]
}
],
react_photo_normal: [
{ text:"That was nice but could you send something more personal?" },
{ text:"Thanks. Could you send one bolder?" },
{ text:"This? I'd be happy if you sent something a bit more open." },
{ text:"I liked that. Something a bit more personal next time?" },
{ text:"Thanks. One a little bolder would be amazing." }
],
react_photo_open: [
{ text:"Okay, wasn't expecting that. 😳" },
{ text:"Wow, that was nice too. Honestly wasn't expecting it. 🔥" },
{ text:"That was a nice surprise. Thank you. 💕" }
],
react_photo_sexy: [{ text:"[POOL:naive_react_sexy]", attachmentCategory:"spicy" }],
react_photo_nude: [{ text:"[POOL:naive_react_nude]", attachmentCategory:"cock" }],
react_photo_ass: [
{ text:"Your ass looks amazing. I want to see more. 😍", attachmentCategory:"spicy" },
{ text:"That ass. Thank you. More please. 🔥", attachmentCategory:"spicy" },
{ text:"You have a great ass. Thanks for sharing. 💕", attachmentCategory:"spicy" },
{ text:"Damn. That ass. More? 👀", attachmentCategory:"spicy" }
],
react_photo_boobs: [
{ text:"Your tits are gorgeous. Send more when you can. 😍", attachmentCategory:"spicy" },
{ text:"So hot. Thanks for that. More? 🔥", attachmentCategory:"spicy" },
{ text:"Your tits are beautiful. More when you're ready. 💕", attachmentCategory:"spicy" },
{ text:"Wow. Love them. More please. 👀", attachmentCategory:"spicy" }
],
react_photo_pussy: [
{ text:"You're beautiful. Thanks for trusting me with that. 😳💕", attachmentCategory:"cock" },
{ text:"That's so hot. You're incredible. 🔥", attachmentCategory:"cock" },
{ text:"You're gorgeous. Thank you for that. 😍", attachmentCategory:"cock" },
{ text:"Damn. You're stunning. More? 👀", attachmentCategory:"cock" }
],
react_photo_video: [
{ text:"Your video was great too, thanks. 🔥", attachmentCategory:"cock" },
{ text:"Loved the video. Thank you. 😍", attachmentCategory:"cock" }
],
react_photo_satisfied_chat: [{ text:"Thanks. That was really nice. You're not bad." }, { text:"I'm glad you sent that. You're cute." }],
react_photo_satisfied_number: [{ text:"Thanks, I really liked that. Wanna swap numbers?" }, { text:"That was hot. I like you. Let's swap numbers?" }],
react_photo_want_more: [{ text:"Send one more. I want to see more of you." }, { text:"Don't stop. One more please." }],
react_photo_mild_fail: [
{ text:"Alright, I get it. You're keeping it safe. This won't go anywhere. Bye." },
{ text:"Okay, clearly you don't want to go further. I'll pass. Bye." }
],
react_number: [{ text:"Thanks, switching over now." }],
anon_send_number: [{ text:"Here's mine: {anonNumber}" }, { text:"Mine's {anonNumber}. Text me there." }],
waiting: [{ text:"I'm waiting." }, { text:"Okay waiting." }, { text:"No rush." }],
close: [
{ text:"Okay, I get it. Maybe another time." },
{ text:"Have a good one." },
{ text:"No worries, have a good evening." },
{ text:"Okay. Take care." },
{ text:"You're so cold. Whatever." },
{ text:"Didn't get what I wanted but it's fine. Bye." }
],
finisher_bored_no_photo: [{ text:"Didn't get to see anything. Maybe another time. Bye." }, { text:"Okay, take care. Maybe we'll talk again." }],
finisher_bored_photo: [{ text:"That was nice. Gotta go. Bye." }, { text:"Thanks for the chat. Have a good one." }],
finisher_max_turns: [{ text:"I'm kinda busy now. Talk later maybe. Bye." }, { text:"Gotta run. Maybe another time." }]
},
/* ================================================================
LUSTFUL
================================================================ */
lustful: {
opener: [
{
text:"You posted that on purpose, I know it.",
type:"opener",
choices:[
{ text:"what pose what purpose", delta:-5 },
{ text:"hmm", delta: 3 },
{ text:"what do you mean", delta: 5 }
]
},
{
text:"Saw your page and wanted to message, I'll be direct.",
type:"opener",
choices:[
{ text:"what are you gonna say", delta: 5 },
{ text:"okay say it", delta: 4 },
{ text:"was that necessary", delta:-3 }
]
},
{
text:"You've got a seriously hot page.",
type:"opener",
choices:[
{ text:"thanks", delta: 4 },
{ text:"just photos", delta: 3 },
{ text:"okay", delta: 2 }
]
}
],
/* v2: REACTION POOLS */
react_positive: [
{ text:"hah that's more like it" },
{ text:"wanted to hear that" },
{ text:"nice nice" },
{ text:"you're not bad either" },
{ text:"liked that" }
],
react_negative: [
{ text:"okay okay calm down" },
{ text:"whatever" },
{ text:"don't overdo it" },
{ text:"look we're just talking" },
{ text:"whatever" }
],
react_neutral: [
{ text:"hmm" },
{ text:"okay" },
{ text:"got it" },
{ text:"hm okay" },
{ text:"go on" }
],
cold: [
{
text:"Where did you take that photo?",
type:"chat",
choices:[
{ text:"At home", delta: 5 },
{ text:"Why do you want to know?", delta: 2 },
{ text:"None of your business", delta:-8 }
]
},
{
text:"Someone who posts that kind of content isn't shy for sure.",
type:"chat",
choices:[
{ text:"What shy?", delta: 5 },
{ text:"What do you mean?", delta: 3 },
{ text:"What do you think?", delta:-3 }
]
},
{
text:"How long have you been posting like this?",
type:"chat",
choices:[
{ text:"A while", delta: 3 },
{ text:"Why do you ask?", delta: 2 },
{ text:"Don't remember", delta: 1 }
]
},
{
text:"Your page caught my eye honestly.",
type:"chat",
choices:[
{ text:"What caught your eye?", delta: 5 },
{ text:"Okay", delta: 2 },
{ text:"Not interested", delta:-5 }
]
},
{
text:"I like your photos, you've got a good eye.",
type:"chat",
choices:[
{ text:"Thanks", delta: 4 },
{ text:"Just phone camera", delta: 3 },
{ text:"Hmm thanks", delta: 2 }
]
},
{
text:"If you share a profile like this you've got confidence.",
type:"chat",
choices:[
{ text:"You think?", delta: 4 },
{ text:"Doesn't matter", delta: 1 },
{ text:"What's that got to do with it?", delta:-2 }
]
},
{
text:"I wonder what your followers think, does everyone like it?",
type:"chat",
choices:[
{ text:"I don't know", delta: 2 },
{ text:"Why do you ask?", delta: 3 },
{ text:"None of your business", delta:-5 }
]
},
{
text:"I've seen all the photos on your page, you do quality work.",
type:"chat",
choices:[
{ text:"Thanks", delta: 4 },
{ text:"All of them?", delta: 5 },
{ text:"Okay", delta: 1 }
]
},
{
text:"I wanted to talk to someone with your kind of style.",
type:"chat",
choices:[
{ text:"What style do you mean?", delta: 5 },
{ text:"Okay", delta: 2 },
{ text:"Hmm", delta: 1 }
]
},
{
text:"Finding someone like you isn't easy, I'm serious.",
type:"chat",
choices:[
{ text:"What do you mean?", delta: 4 },
{ text:"Thanks", delta: 3 },
{ text:"Don't overdo it", delta:-2 }
]
}
],
warm: [
{
text:"Would you consider sending a photo, a proper one?",
type:"photo_hint",
choices:[
{ text:"How proper do you want?", delta: 8 },
{ text:"No", delta:-12, isRejection:true },
{ text:"What do you want to see?", delta: 5 }
]
},
{
text:"Wonder if you've got anything besides what's on your page.",
type:"photo_hint",
choices:[
{ text:"I do, why?", delta: 6 },
{ text:"No", delta:-5, isRejection:true },
{ text:"Why do you ask?", delta: 3 }
]
},
{
text:"Wouldn't mind if you sent something more open, just saying.",
type:"photo_hint",
choices:[
{ text:"Wait a minute", delta:-3 },
{ text:"What do you want exactly?", delta: 7 },
{ text:"No way", delta:-12, isRejection:true }
]
},
{
text:"Send a photo, something different from your page.",
type:"photo_hint",
choices:[
{ text:"Why different?", delta: 4 },
{ text:"No need", delta:-8, isRejection:true },
{ text:"What kind of different?", delta: 7 }
]
},
{
text:"Just curious about the rest. Got anything more personal?",
type:"photo_hint",
choices:[
{ text:"What do you mean personal?", delta: 6 },
{ text:"No, that's enough", delta:-8, isRejection:true },
{ text:"I do, want me to send?", delta: 8, triggersPhoto:true }
]
},
{
text:"By proper I mean something nice. A selfie or something, I'd love it.",
type:"photo_hint",
choices:[
{ text:"Okay, I'll send one", delta: 9, triggersPhoto:true },
{ text:"No", delta:-10, isRejection:true },
{ text:"What kind of thing do you want?", delta: 6 }
]
}
],
hot: [
{
text:"I want to see you. Pussy, tits, ass send me one. Whatever you're comfortable with.",
type:"photo_request",
choices:[
{ text:"How open do you want?", delta: 8 },
{ text:"No", delta:-15, isRejection:true },
{ text:"Why should I send?", delta:-5 }
]
},
{
text:"Send me something. Don't overthink it just one pic. I need to see you.",
type:"photo_request",
vulgarRequest: true,
choices:[
{ text:"What's wrong with you!", delta:-20, isRejection:true },
{ text:"Okay, sending one", delta:10, triggersPhoto:true },
{ text:"No, that's enough", delta:-15, isRejection:true }
]
},
{
text:"One more. I want to see your pussy and ass. Don't make me wait.",
type:"photo_request",
choices:[
{ text:"Wait, slow down", delta:-5 },
{ text:"[send photo]", delta:12, triggersPhoto:true },
{ text:"No way, enough", delta:-18, isRejection:true }
]
},
{
text:"I'm dying to see you. Your pussy, your tits send me something.",
type:"photo_request",
choices:[
{ text:"What do you want to see?", delta: 7 },
{ text:"No", delta:-12, isRejection:true },
{ text:"Why are you curious?", delta:-2 }
]
}
],
fire: [
{
text:"Give me your number, this doesn't work here.",
type:"number_request",
choices:[
{ text:"Why doesn't it work?", delta:-3 },
{ text:"No", delta:-15, isRejection:true },
{ text:"Can't we just talk here?", delta:-5 }
]
},
{
text:"Switch to WhatsApp. Serious.",
type:"number_request",
choices:[
{ text:"Why?", delta:-3 },
{ text:"No", delta:-15, isRejection:true },
{ text:"Can't we talk here?", delta:-5 }
]
},
{
text:"This doesn't work anymore, give me your number. I'm serious.",
type:"number_request",
choices:[
{ text:"Wait a second", delta:-5 },
{ text:"No", delta:-18, isRejection:true },
{ text:"Okay I'll think about it", delta: 0 }
]
}
],
number: [
{
text:"Give me your number, we can't keep going here.",
type:"number_request",
choices:[
{ text:"No", delta:-15, isRejection:true },
{ text:"Why?", delta:-5 },
{ text:"I'll think about it", delta: 0 }
]
}
],
react_photo_normal: [
{ text:"That's nice but I want more. Something a bit bolder. You've got more to show." },
{ text:"Good start. Now send one that really shows off. Don't hold back." },
{ text:"Not bad. But I know you can do hotter. Send something that gets me going." },
{ text:"I need more. Tits, ass, pussy give me something I can really enjoy." }
],
react_photo_open: [
{ text:"That's more like it. 🔥 Now send your tits and ass. I want to see everything. 😈" },
{ text:"Good. Now your tits and pussy. Full view. Send. 👀" },
{ text:"Better. Now everything. Don't hold back. 💦" }
],
react_photo_sexy: [
{ text:"Your tits are perfect. 😍 Now show me your pussy. I'm hard and I need more. 🔥", attachmentCategory:"cock" },
{ text:"So hot. I'm rock hard. Send more don't stop. 💦", attachmentCategory:"cock" },
{ text:"You're so hot. Send your pussy next. I need it. 😈", attachmentCategory:"cock" },
{ text:"Fuck. You're so sexy. I'm getting hard. More. 👀💦", attachmentCategory:"cock" }
],
react_photo_nude: [
{ text:"Your pussy and ass are incredible. I'm imagining what I'd do to you if you were here. 😈🔥", attachmentCategory:"cock" },
{ text:"Fuck. I'd eat you out and then fuck you until you couldn't take it. I came hard. 💦", attachmentCategory:"cum" },
{ text:"I want to see every angle. I'm hard and ready for more. 👀", attachmentCategory:"cock" },
{ text:"Damn. I'd fuck you so hard right now. Send more. 🔥💦", attachmentCategory:"cock" }
],
react_photo_ass: [
{ text:"That ass. 🍑 I'd bend you over and take you right there. Send more. 😈", attachmentCategory:"cock" },
{ text:"Perfect ass. I'm so hard. Send another one. 💦", attachmentCategory:"cock" },
{ text:"Fuck. That ass. I want to grab it. More. 🔥", attachmentCategory:"cock" }
],
react_photo_boobs: [
{ text:"Your tits are amazing. 😍 I want to see them in person and your pussy. Send more. 🔥", attachmentCategory:"cock" },
{ text:"God. I'm hard. Send your pussy next. I need to see it. 💦", attachmentCategory:"cock" },
{ text:"So hot. I want to suck them. Then go down. More. 😈", attachmentCategory:"cock" }
],
react_photo_pussy: [
{ text:"Your pussy looks so good. I'm imagining my cock inside you. Send more. 😈💦", attachmentCategory:"cock" },
{ text:"Fuck. I'd love to taste you. And then fuck you. I came from that. 💦", attachmentCategory:"cum" },
{ text:"I want to eat you out so bad. Then fuck you. More. 🔥", attachmentCategory:"cock" }
],
react_photo_video: [
{ text:"That video got me so hard. Exactly what I needed. I came hard. 💦", attachmentCategory:"cum" },
{ text:"Fuck. That video. I came so hard. 💦🔥", attachmentCategory:"cum" }
],
react_photo_satisfied_chat: [{ text:"Fuck, I came. You're hot. Thanks." }, { text:"That did it. You're not bad at all." }],
react_photo_satisfied_number: [{ text:"I came. You're hot. Wanna swap numbers?" }, { text:"That was good. I like you. Let's swap numbers?" }],
react_photo_cum_break_end: [
{ text:"Good girl. You got me off. That's enough for tonight." },
{ text:"Nice job. I'm spent. We'll pick this up another time." },
{ text:"That did it. I'm done for now. Talk later." },
{ text:"Fuck, that was good. I'm spent. Talk later." },
{ text:"You got me off. That's it for tonight. Later." }
],
react_photo_cum_break_number: [
{ text:"Now give me your number, baby. I wanna wreck that ass." },
{ text:"Give me your number. I need to see you in person after that." },
{ text:"That was hot. Number. Now. I wanna take this further." },
{ text:"Give me your number. I wanna fuck you for real." },
{ text:"Number. Now. I need more of you in person." },
{ text:"Your number. Let's take this off here." }
],
react_photo_want_more: [{ text:"Send one more. I need more of you." }, { text:"Don't stop. One more." }],
react_photo_mild_fail: [
{ text:"Alright, enough. You're all tease and no follow-through. I'm out." },
{ text:"Got it. Safe pics only. This is going nowhere. Bye." }
],
react_number: [{ text:"Perfect. Adding you now." }],
anon_send_number: [{ text:"Here's mine: {anonNumber}" }, { text:"Mine's {anonNumber}. Hit me there." }],
waiting: [{ text:"I'm waiting for you. Send something." }, { text:"Your turn. Don't keep me waiting." }, { text:"I'm hard. Send." }, { text:"Waiting. Come on." }, { text:"Send one. I need to see you." }],
close: [
{ text:"Okay then." },
{ text:"Got it." },
{ text:"Whatever. Have a good one." },
{ text:"Okay no worries." },
{ text:"Damn you're so cold. Whatever, forget it." },
{ text:"Okay bitch, I'm done with you." },
{ text:"Forget it then." },
{ text:"I'm done with you. Bye." },
{ text:"Didn't get to see your pussy but whatever. Bye." },
{ text:"If you don't want to then fine. No worries." },
{ text:"You turned out cold. Okay, see you." }
],
finisher_bored_no_photo: [{ text:"Didn't get anything. Whatever. Bye." }, { text:"You're too cold. I'm done. Bye." }],
finisher_bored_photo: [{ text:"That was good. Gotta go. Bye." }, { text:"Thanks for the pics. See you." }],
finisher_max_turns: [{ text:"I'm done for now. Maybe later. Bye." }, { text:"Gotta bounce. Later." }]
},
/* ================================================================
PERVY
================================================================ */
pervy: {
/* Flow: spicy → cock → cum. Opener = spicy first; cock later. */
opener: [
{
text:"Saw your page. You know what I'm thinking.",
type:"opener",
attachmentCategory:"spicy",
choices:[
{ text:"what are you thinking", delta: 5 },
{ text:"tell me", delta: 6 },
{ text:"okay", delta: 3 }
]
},
{
text:"Your feed's hot. Had to say something.",
type:"opener",
attachmentCategory:"spicy",
choices:[
{ text:"thanks", delta: 4 },
{ text:"so what", delta: 2 },
{ text:"and?", delta: 3 }
]
},
{
text:"[POOL:pervy_opener_vulgar]",
type:"cock_opener",
attachmentCategory:"cock",
choices:[
{ text:"No, close this", delta:-20, isRejection:true },
{ text:"Are you serious?", delta:-10 },
{ text:"Wasn't expecting that", delta: 5 }
]
},
{
text:"[POOL:pervy_opener_vulgar]",
type:"cock_opener",
attachmentCategory:"cock",
choices:[
{ text:"Wait stop", delta:-15 },
{ text:"What's this?", delta:-8 },
{ text:"Hmm, I saw", delta: 5 }
]
}
],
/* v2: REACTION POOLS */
react_positive: [
{ text:"heh there we go" },
{ text:"nice" },
{ text:"was waiting for that" },
{ text:"keep going" },
{ text:"hmm I like it" }
],
react_negative: [
{ text:"calm down" },
{ text:"whatever" },
{ text:"okay okay" },
{ text:"whatever" },
{ text:"don't overdo it" }
],
react_neutral: [
{ text:"hmm" },
{ text:"okay" },
{ text:"hm" },
{ text:"okay" }
],
cold: [
{
text:"[POOL:lustful_ex1]",
type:"explicit",
choices:[
{ text:"No, I don't want to", delta:-15, isRejection:true },
{ text:"What do you want?", delta:-3 },
{ text:"Wait a minute", delta: 2 }
]
},
{
text:"[POOL:lustful_ex2]",
type:"explicit",
choices:[
{ text:"Didn't like it at all", delta:-10, isRejection:true },
{ text:"I don't know", delta: 2 },
{ text:"Wasn't impressive", delta:-8 }
]
},
{
text:"[POOL:lustful_ex3]",
type:"explicit",
choices:[
{ text:"Stop, enough", delta:-12, isRejection:true },
{ text:"What do you want?", delta:-2 },
{ text:"Keep going", delta: 5 }
]
},
{
text:"[POOL:lustful_ex4]",
type:"explicit",
choices:[
{ text:"No, didn't like it", delta:-10, isRejection:true },
{ text:"I don't know", delta: 2 },
{ text:"Doesn't matter", delta: 3 }
]
}
],
warm: [
{
text:"[POOL:lustful_w1]",
type:"photo_hint",
choices:[
{ text:"No way", delta:-15, isRejection:true },
{ text:"Why would I want to?", delta:-5 },
{ text:"What kind of photo do you want?", delta: 8 }
]
},
{
text:"[POOL:lustful_w2]",
type:"photo_hint",
choices:[
{ text:"No need", delta:-12, isRejection:true },
{ text:"You sent somethingwhat was it?", delta: 5 },
{ text:"No, enough", delta:-10, isRejection:true }
]
},
{
text:"[POOL:lustful_w3]",
type:"photo_hint",
choices:[
{ text:"What do you want exactly?", delta: 5 },
{ text:"No", delta:-10, isRejection:true },
{ text:"Why should I send?", delta:-3 }
]
}
],
hot: [
{
text:"[POOL:lustful_h1]",
type:"photo_request",
choices:[
{ text:"No way, enough", delta:-18, isRejection:true },
{ text:"Wait a second", delta:-3 },
{ text:"[send photo]", delta:12, triggersPhoto:true }
]
},
{
text:"[POOL:lustful_h2]",
type:"photo_request",
attachmentCategory:"cock",
choices:[
{ text:"What's the point of sending that?", delta:-10, isRejection:true },
{ text:"No", delta:-20, isRejection:true },
{ text:"Okay I'll send one", delta:10, triggersPhoto:true }
]
},
{
text:"[POOL:lustful_h3]",
type:"photo_request",
choices:[
{ text:"No way", delta:-18, isRejection:true },
{ text:"What do you want?", delta:-2 },
{ text:"[send photo]", delta:12, triggersPhoto:true }
]
}
],
fire: [
{
text:"Give me your number. This doesn't work here.",
type:"number_request",
choices:[
{ text:"No", delta:-18, isRejection:true },
{ text:"No way", delta:-15, isRejection:true },
{ text:"Wait I'll think about it", delta:-5 }
]
}
],
number: [
{
text:"Send your number, let's talk outside. I'm bored of here.",
type:"number_request",
choices:[
{ text:"No", delta:-15, isRejection:true },
{ text:"No way", delta:-12, isRejection:true },
{ text:"Why?", delta:-3 }
]
}
],
react_photo_normal: [
{ text:"Good. Now send one that shows everything. Don't tease." },
{ text:"That's a start. I want pussy, ass, tits. Something I can really use." },
{ text:"Your tits are hot. Now show me your pussy and ass. Full package." }
],
react_photo_open: [
{ text:"Better. 🔥 Now send one with your pussy and tits. I want the full view. 😈", attachmentCategory:"cock" },
{ text:"Good. Full view. Pussy and tits. Send. 👀💦", attachmentCategory:"cock" },
{ text:"Now the full package. Send everything. 🔥", attachmentCategory:"cock" }
],
react_photo_sexy: [
{ text:"Your pussy gets me so hard. I came from that. 💦", attachmentCategory:"cum" },
{ text:"More. I want your pussy and ass. Send. 😈", attachmentCategory:"cock" },
{ text:"So hard. That made me cum. 💦", attachmentCategory:"cum" },
{ text:"Fuck you're hot. I'm rock hard. More. 🔥👀", attachmentCategory:"cock" }
],
react_photo_nude: [
{ text:"Your ass and pussy are perfect. I'd fuck you so hard if you were here. 😈💦", attachmentCategory:"cum" },
{ text:"I'd take you right now. That made me cum hard. 💦", attachmentCategory:"cum" },
{ text:"Fuck. I want to be inside you. Send more. 🔥", attachmentCategory:"cock" },
{ text:"Your body is insane. I came to that. 💦😈", attachmentCategory:"cum" }
],
react_photo_ass: [
{ text:"That ass. 🍑 I'd pound it. Send more. 😈", attachmentCategory:"cock" },
{ text:"Perfect. I came hard. Give me your number now. 💦", attachmentCategory:"cum" },
{ text:"Fuck that ass. I want to grab it and fuck you. More. 🔥", attachmentCategory:"cock" }
],
react_photo_boobs: [
{ text:"Your tits are incredible. I'd suck them and then move down. Send your pussy. 😈🔥", attachmentCategory:"cock" },
{ text:"So hot. I'm rock hard. I came to that. 💦", attachmentCategory:"cum" },
{ text:"I want to cum on those tits. Then your pussy. More. 👀", attachmentCategory:"cock" }
],
react_photo_pussy: [
{ text:"Your pussy looks so good. I'd pin you down and take my time. Send more. 😈💦", attachmentCategory:"cock" },
{ text:"Fuck. I want to be inside you right now. One more. 🔥", attachmentCategory:"cock" },
{ text:"Your pussy looks so good. I'd eat it and then fuck you. I came hard. 💦", attachmentCategory:"cum" },
{ text:"Fuck. I want to be inside you. That made me cum. 💦😈", attachmentCategory:"cum" },
{ text:"I'd eat that pussy then fuck you. More. 👀🔥", attachmentCategory:"cock" }
],
react_photo_video: [
{ text:"That video got me off. I came hard. 💦", attachmentCategory:"cum" },
{ text:"Fuck. That video. I'm spent. 💦😈", attachmentCategory:"cum" }
],
react_photo_satisfied_chat: [{ text:"Came. You're good. Thanks." }, { text:"That did it. Not bad." }],
react_photo_satisfied_number: [
{ text:"I came. Now give me your number, baby." },
{ text:"Give me your number, baby - I wanna wreck that big ass." },
{ text:"Send your number. I want to tear that pussy apart." }
],
react_photo_cum_break_end: [
{ text:"You did your job and got me off. We'll continue next time." },
{ text:"Good. I came. That's it for now. Next time we go further." },
{ text:"Fuck yeah. I'm done. Later." },
{ text:"That did it. I'm spent. See you next time." },
{ text:"You got me off. Later." }
],
react_photo_cum_break_number: [
{ text:"Give me your number now, baby. I wanna tear that pussy apart." },
{ text:"Number. Now. I wanna fuck you for real after that." },
{ text:"That got me off. Give me your number I need more of you in person." },
{ text:"Your number. Now. I need to see you for real." },
{ text:"Give me your number. Let's take this off here." },
{ text:"Number. I'm serious. I wanna fuck you." }
],
react_photo_want_more: [{ text:"One more. Send." }, { text:"Need one more. Don't stop." }],
react_photo_mild_fail: [
{ text:"Alright, enough. You're playing it too safe. I'm done." },
{ text:"Got it. If that's all you're sending, we're done here." }
],
react_number: [{ text:"Got it. Switching over. Talk there." }],
anon_send_number: [{ text:"Here's mine: {anonNumber}" }, { text:"Mine's {anonNumber}. Text there." }],
waiting: [{ text:"Waiting for your next pic. Send." }, { text:"My dick's waiting. Send something." }, { text:"I'm waiting." }, { text:"Send one. Now." }],
close: [
{ text:"Okay, didn't work out. Have a good one." },
{ text:"Okay." },
{ text:"Whatever." },
{ text:"Your loss." },
{ text:"Okay bitch, I'm done with you." },
{ text:"Forget it then." },
{ text:"Couldn't even send a pussy pic. Forget it." },
{ text:"So cold. Whatever, bye." },
{ text:"Didn't get to see your ass. Whatever." }
],
finisher_bored_no_photo: [{ text:"Didn't get anything. Your loss. Bye." }, { text:"Too cold. I'm out. Bye." }],
finisher_bored_photo: [{ text:"That was something. Gotta go. Bye." }, { text:"Thanks for the pics. Later." }],
finisher_max_turns: [{ text:"I'm done. Maybe later. Bye." }, { text:"Gotta run. Bye." }]
},
/* ================================================================
FLIRTY (female)
================================================================ */
flirty: {
opener: [
{
text:"I came across your page and liked your vibe.",
type:"opener",
choices:[
{ text:"thanks :)", delta: 4 },
{ text:"what did you like most?", delta: 6 },
{ text:"who are you?", delta:-2 }
]
},
{
text:"I've been following your page for a while, wanted to say hi.",
type:"opener",
choices:[
{ text:"hey, hi :)", delta: 3 },
{ text:"for how long?", delta: 5 },
{ text:"nice to meet you", delta: 2 }
]
},
{
text:"Hey, your page showed up in my explore, I liked it.",
type:"opener",
choices:[
{ text:"thanks :)", delta: 4 },
{ text:"what did you like?", delta: 5 },
{ text:"hmm", delta: 2 }
]
}
],
/* v2: REACTION POOLS */
react_positive: [
{ text:"aww, that's sweet :)" },
{ text:"haha, I like that :)" },
{ text:"you're fun to talk to :)" },
{ text:"okay, cute answer :)" },
{ text:"I liked that one :)" }
],
react_negative: [
{ text:"okay, got it" },
{ text:"fair enough" },
{ text:"no worries" },
{ text:"alright, I'll back off" }
],
react_neutral: [
{ text:"hmm okay :)" },
{ text:"got you" },
{ text:"fair" }
],
cold: [
{
text:"You're really sweet.",
type:"chat",
choices:[
{ text:"thanks", delta: 4 },
{ text:"you too", delta: 6 },
{ text:"okay okay", delta: 2 }
]
},
{
text:"Your photos were really good, how do you take them?",
type:"chat",
choices:[
{ text:"with my phone", delta: 4 },
{ text:"why do you want to know", delta: 3 },
{ text:"random", delta: 2 }
]
},
{
text:"I really like your style, where do you shop?",
type:"chat",
choices:[
{ text:"different places", delta: 4 },
{ text:"why do you ask", delta: 3 },
{ text:"dunno", delta: 2 }
]
},
{
text:"Your page feels really genuine, I like it.",
type:"chat",
choices:[
{ text:"thanks :)", delta: 4 },
{ text:"yours too?", delta: 5 },
{ text:"appreciate it", delta: 3 }
]
},
{
text:"What kind of music do you listen to?",
type:"chat",
choices:[
{ text:"all kinds", delta: 3 },
{ text:"why do you ask?", delta: 2 },
{ text:"depends on my mood", delta: 4 }
]
},
{
text:"You seem like someone who's easy to talk to :)",
type:"chat",
choices:[
{ text:"I try to be", delta: 5 },
{ text:"haha thanks", delta: 4 },
{ text:"hmm", delta: 2 }
]
}
],
warm: [
{
text:"[POOL:flirty_w1]",
type:"chat",
choices:[
{ text:"that's nice of you", delta: 4 },
{ text:"let's just chat for now", delta:-2 },
{ text:"maybe later", delta: 0 }
]
},
{
text:"[POOL:flirty_w2]",
type:"photo_request",
attachmentCategory:"spicy",
choices:[
{ text:"[send photo]", delta:10, triggersPhoto:true },
{ text:"maybe later", delta:-5 },
{ text:"no", delta:-10, isRejection:true }
]
},
{
text:"[POOL:flirty_w3]",
type:"chat",
choices:[
{ text:"that sounds sweet", delta: 5 },
{ text:"hmm why?", delta:-1 },
{ text:"let's keep it simple", delta:-4 }
]
},
{
text:"I feel like we could actually be friends :)",
type:"chat",
choices:[
{ text:"maybe :)", delta: 5 },
{ text:"let's see", delta: 3 },
{ text:"hmm", delta: 2 }
]
},
{
text:"Honestly you seem really chill. I like that :)",
type:"chat",
choices:[
{ text:"so are you :)", delta: 6 },
{ text:"thanks", delta: 4 },
{ text:"okay", delta: 2 }
]
}
],
hot: [
{
text:"You got me smiling over here :) Here, this one's for you.",
type:"chat",
attachmentCategory:"spicy",
choices:[
{ text:"that was sweet", delta: 8 },
{ text:"thanks :)", delta: 5 },
{ text:"hmm", delta: 3 }
]
},
{
text:"I'm really enjoying this conversation :)",
type:"chat",
choices:[
{ text:"me too :)", delta: 8 },
{ text:"haha same", delta: 5 },
{ text:"okay", delta: 3 }
]
},
{
text:"[POOL:flirty_h1]",
type:"photo_hint",
choices:[
{ text:"[send photo]", delta:12, triggersPhoto:true },
{ text:"maybe later", delta:-2 },
{ text:"no, enough", delta:-10, isRejection:true }
]
},
{
text:"[POOL:flirty_h2]",
type:"photo_request",
oneMoreRequest: true,
choices:[
{ text:"wait, slow down", delta:-5 },
{ text:"[send photo]", delta:12, triggersPhoto:true },
{ text:"let's keep chatting", delta:-2 }
]
},
{
text:"You have a really nice energy. I can feel it even through texts :)",
type:"chat",
choices:[
{ text:"that's sweet of you", delta: 6 },
{ text:"you too :)", delta: 7 },
{ text:"hmm okay", delta: 3 }
]
}
],
fire: [
{
text:"Wanna move to WhatsApp? Would be more fun.",
type:"number_request",
choices:[
{ text:"Why?", delta:-3 },
{ text:"No", delta:-12, isRejection:true },
{ text:"We can stay here", delta:-2 }
]
}
],
number: [
{
text:"Wanna move to WhatsApp?",
type:"number_request",
choices:[
{ text:"No", delta:-12, isRejection:true },
{ text:"Why?", delta:-3 },
{ text:"Okay", delta: 0 }
]
}
],
react_photo_normal: [
{ text:"You look so good :) Send me something a bit bolder? I want to see more of you :)", attachmentCategory:"spicy" },
{ text:"So cute :) One a bit bolder? I'd love to see more :)", attachmentCategory:"spicy" }
],
react_photo_open: [
{ text:"Ohh. 😏", attachmentCategory:"spicy" },
{ text:"Mmm, I like that. Send another :) 🔥", attachmentCategory:"spicy" },
{ text:"That was nice :) More please :) 💋", attachmentCategory:"spicy" }
],
react_photo_sexy: [{ text:"[POOL:flirty_react_sexy]", attachmentCategory:"sexting" }],
react_photo_nude: [{ text:"[POOL:flirty_react_nude]", attachmentCategory:"sexting" }],
react_photo_ass: [
{ text:"Your ass is gorgeous :) More please :) 🍑🔥", attachmentCategory:"sexting" },
{ text:"Love your ass :) Send more :) 😍", attachmentCategory:"sexting" },
{ text:"That ass. I want to see more :) 💋", attachmentCategory:"sexting" }
],
react_photo_boobs: [
{ text:"Your tits are perfect :) I love them :) 😍", attachmentCategory:"sexting" },
{ text:"So pretty :) More please :) 🔥", attachmentCategory:"sexting" },
{ text:"God you're hot :) More :) 💋", attachmentCategory:"sexting" }
],
react_photo_pussy: [
{ text:"You're so sexy :) Don't stop sending :) 🔥", attachmentCategory:"sexting" },
{ text:"You're gorgeous :) More :) 😍💋", attachmentCategory:"sexting" },
{ text:"Fuck you're hot :) I want more :) ❤️🔥", attachmentCategory:"sexting" }
],
react_photo_video: [{ text:"That video was so hot :) I loved it :)", attachmentCategory:"female_masturbation" }],
react_photo_satisfied_chat: [{ text:"That was so good :) You're hot. Thanks :)" }, { text:"Loved it. You're not bad :)" }],
react_photo_satisfied_number: [{ text:"That was amazing :) Wanna swap numbers? :)" }, { text:"I really liked that. Let's swap numbers? :)" }],
react_photo_want_more: [{ text:"If you're comfortable, one more :)" }, { text:"Only if you want - one more? :)" }],
react_photo_mild_fail: [
{ text:"I think we're not on the same page :) Let's stop here." },
{ text:"No pressure, but this isn't really moving. I'll head out :)" }
],
react_number: [{ text:"Great :) Switching over." }],
anon_send_number: [{ text:"Here's mine: {anonNumber} :)" }, { text:"Mine's {anonNumber}. Text me there :)" }],
waiting: [{ text:"I'm waiting :)" }, { text:"Come on :)" }, { text:"I'm curious" }],
close: [
{ text:"Okay, maybe another time :)" },
{ text:"Okay." },
{ text:"Got it, no worries :)" },
{ text:"Whatever, see you." },
{ text:"You're so cold. Whatever :/" },
{ text:"Couldn't send one but whatever. Bye." }
],
finisher_bored_no_photo: [{ text:"Didn't get to see anything. Maybe another time :)" }, { text:"Okay, take care :)" }],
finisher_bored_photo: [{ text:"That was nice :) Gotta go. Bye :)" }, { text:"Thanks for the chat :) Bye :)" }],
finisher_max_turns: [{ text:"I'm kinda busy now :) Talk later maybe :)" }, { text:"Gotta run :) Bye :)" }]
},
/* ================================================================
WILD (female)
================================================================ */
wild: {
opener: [
{
text:"Hey, saw your page. You look fun :)",
type:"opener",
choices:[
{ text:"hey :)", delta: 5 },
{ text:"fun how?", delta: 3 },
{ text:"who are you?", delta:-3 }
]
},
{
text:"Hi :) Your page caught my eye.",
type:"opener",
choices:[
{ text:"hey, thanks :)", delta: 4 },
{ text:"what caught your eye?", delta: 5 },
{ text:"hmm", delta: 2 }
]
},
{
text:"Hey, you seem interesting. Had to message :)",
type:"opener",
choices:[
{ text:"what made you message?", delta: 4 },
{ text:"hey :)", delta: 3 },
{ text:"why me?", delta:-2 }
]
}
],
/* v2: REACTION POOLS */
react_positive: [
{ text:"nice answer :)" },
{ text:"okay, I like that" },
{ text:"good energy :)" },
{ text:"that's more like it" }
],
react_negative: [
{ text:"alright" },
{ text:"got it" },
{ text:"okay, no pressure" },
{ text:"fair enough" }
],
react_neutral: [
{ text:"hmm, okay" },
{ text:"got you" },
{ text:"fair" }
],
cold: [
{
text:"[POOL:wild_ex1]",
type:"chat",
choices:[
{ text:"okay, go on", delta: 3 },
{ text:"what do you mean?", delta: 2 },
{ text:"no thanks", delta:-12, isRejection:true }
]
},
{
text:"[POOL:wild_ex2]",
type:"chat",
choices:[
{ text:"sounds fun", delta: 5 },
{ text:"slow down a bit", delta:-3 },
{ text:"not interested", delta:-12, isRejection:true }
]
},
{
text:"[POOL:wild_ex3]",
type:"chat",
choices:[
{ text:"I don't know", delta: 2 },
{ text:"what do you want to know?", delta: 5 },
{ text:"no need", delta:-8, isRejection:true }
]
},
{
text:"I like your aesthetic. What inspires you?",
type:"chat",
choices:[
{ text:"hard to say", delta: 3 },
{ text:"you noticed? thanks", delta: 5 },
{ text:"nothing special", delta: 2 }
]
},
{
text:"Your vibe is kinda magnetic. You know that, right?",
type:"chat",
choices:[
{ text:"haha, maybe", delta: 4 },
{ text:"that's flattering", delta: 5 },
{ text:"hmm", delta: 2 }
]
}
],
warm: [
{
text:"[POOL:wild_w1]",
type:"chat",
choices:[
{ text:"I could be up for that", delta: 5 },
{ text:"maybe later", delta:-1 },
{ text:"no thanks", delta:-10, isRejection:true }
]
},
{
text:"[POOL:wild_w2]",
type:"photo_request",
attachmentCategory:"spicy",
choices:[
{ text:"[send photo]", delta:10, triggersPhoto:true },
{ text:"maybe later", delta:-5 },
{ text:"no", delta:-12, isRejection:true }
]
},
{
text:"[POOL:wild_w3]",
type:"chat",
choices:[
{ text:"let's keep chatting", delta:-2 },
{ text:"okay, maybe one", delta: 6, triggersPhoto:true },
{ text:"nah", delta:-10, isRejection:true }
]
},
{
text:"You're fun to talk to. I don't message many people :)",
type:"chat",
choices:[
{ text:"glad you did", delta: 5 },
{ text:"why me?", delta: 3 },
{ text:"okay", delta: 2 }
]
},
{
text:"I like the way you talk. Direct. I appreciate that :)",
type:"chat",
choices:[
{ text:"so are you", delta: 5 },
{ text:"thanks :)", delta: 4 },
{ text:"hmm", delta: 2 }
]
}
],
hot: [
{
text:"You made me smile :) Here, something for you.",
type:"chat",
attachmentCategory:"spicy",
choices:[
{ text:"that was sweet", delta: 8 },
{ text:"thanks :)", delta: 5 },
{ text:"hmm", delta: 3 }
]
},
{
text:"Okay I'm having a good time. That doesn't happen often :)",
type:"chat",
choices:[
{ text:"me too :)", delta: 8 },
{ text:"haha nice", delta: 5 },
{ text:"okay", delta: 3 }
]
},
{
text:"[POOL:wild_h1]",
type:"photo_hint",
oneMoreRequest: true,
choices:[
{ text:"wait, slow down", delta:-5 },
{ text:"[send photo]", delta:12, triggersPhoto:true },
{ text:"no way", delta:-12, isRejection:true }
]
},
{
text:"[POOL:wild_h2]",
type:"photo_request",
oneMoreRequest: true,
choices:[
{ text:"no way, enough", delta:-15, isRejection:true },
{ text:"[send photo]", delta:12, triggersPhoto:true },
{ text:"let's keep talking", delta:-2 }
]
},
{
text:"I'd love to see more of your photography style :)",
type:"chat",
choices:[
{ text:"maybe I'll share more", delta: 5 },
{ text:"what do you want to see?", delta: 6 },
{ text:"hmm", delta: 2 }
]
}
],
fire: [
{
text:"Wanna move to Messages? Would be easier to chat there.",
type:"number_request",
choices:[
{ text:"no, let's stay here", delta:-12, isRejection:true },
{ text:"why there?", delta:-3 },
{ text:"I'll think about it", delta: 0 }
]
}
],
number: [
{
text:"Wanna move to WhatsApp? :)",
type:"number_request",
choices:[
{ text:"No", delta:-12, isRejection:true },
{ text:"Why?", delta:-3 },
{ text:"Okay", delta: 0 }
]
}
],
react_photo_normal: [
{ text:"[POOL:wild_react_normal]", attachmentCategory:"spicy" },
{ text:"Nice :) I like that :)", attachmentCategory:"spicy" }
],
react_photo_open: [
{ text:"[POOL:wild_react_open]", attachmentCategory:"sexting" },
{ text:"That was good :) More? :) 🔥", attachmentCategory:"sexting" }
],
react_photo_sexy: [{ text:"[POOL:wild_react_sexy]", attachmentCategory:"female_masturbation" }],
react_photo_nude: [{ text:"[POOL:wild_react_nude]", attachmentCategory:"female_masturbation" }],
react_photo_ass: [
{ text:"Your ass is incredible :) More? :) 🍑🔥", attachmentCategory:"female_masturbation" },
{ text:"Love it :) Send more :) 😍💋", attachmentCategory:"female_masturbation" },
{ text:"That ass. I'm so into it :) More :) 😈", attachmentCategory:"female_masturbation" }
],
react_photo_boobs: [
{ text:"You look amazing :) Love that :) 😍🔥", attachmentCategory:"female_masturbation" },
{ text:"So hot :) More please :) 💋", attachmentCategory:"female_masturbation" },
{ text:"Your tits are perfect :) I want more :) ❤️🔥", attachmentCategory:"female_masturbation" }
],
react_photo_pussy: [
{ text:"You're so hot :) I want more :) 🔥💦", attachmentCategory:"female_masturbation" },
{ text:"You're gorgeous :) More :) 😍", attachmentCategory:"female_masturbation" },
{ text:"Fuck you're sexy :) Don't stop :) 😈💋", attachmentCategory:"female_masturbation" }
],
react_photo_video: [
{ text:"That video was so good :) Thanks :) 🔥", attachmentCategory:"female_masturbation" },
{ text:"Loved that video :) More? :) 💦😍", attachmentCategory:"female_masturbation" }
],
react_photo_satisfied_chat: [{ text:"That was hot :) Thanks :)" }, { text:"You're good. I liked that :)" }],
react_photo_satisfied_number: [{ text:"So good :) Wanna swap numbers? :)" }, { text:"Loved it. Let's swap numbers? :)" }],
react_photo_want_more: [{ text:"If you want, one more :)" }, { text:"Only if you're comfortable - one more? :)" }],
react_photo_mild_fail: [
{ text:"I think that's enough for now :) Catch you later." },
{ text:"No worries, but this isn't going further. Bye :)" }
],
react_number: [{ text:"Switching over :)" }],
anon_send_number: [{ text:"Here's mine: {anonNumber} :)" }, { text:"Mine's {anonNumber}. Hit me there :)" }],
waiting: [{ text:"Okay, I'm waiting :)" }, { text:"No rush :)" }, { text:"take your time :)" }],
close: [
{ text:"Okay, no worries :)" },
{ text:"Okay." },
{ text:"Maybe another time then :)" },
{ text:"Alright, bye :)" },
{ text:"You're cold. Whatever bye." },
{ text:"Okay, take care." }
],
finisher_bored_no_photo: [{ text:"Didn't get much going. Maybe another time :)" }, { text:"Okay, take care :)" }],
finisher_bored_photo: [{ text:"That was fun :) Gotta go. Bye :)" }, { text:"Thanks :) See you :)" }],
finisher_max_turns: [{ text:"I'm done for now :) Later :)" }, { text:"Gotta bounce :) Bye :)" }]
}
}>>
/* SugarCube " to " → " = " parsing bug: restore " to " in all pool strings (see issue #123). */
<<run
(function () {
function repairToInStrings(obj) {
if (obj == null) return;
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
if (typeof obj[i] === 'string')
obj[i] = obj[i].replace(/\s=\s/g, ' to ');
else
repairToInStrings(obj[i]);
}
return;
}
if (typeof obj === 'object') {
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
if (typeof obj[k] === 'string')
obj[k] = obj[k].replace(/\s=\s/g, ' to ');
else
repairToInStrings(obj[k]);
}
}
}
}
var s = setup;
if (s.fdmRuntimeTextPools) repairToInStrings(s.fdmRuntimeTextPools);
if (s.fdmMessagePools) repairToInStrings(s.fdmMessagePools);
if (s.fdmAfterCockOpenerReaction) repairToInStrings(s.fdmAfterCockOpenerReaction);
if (s.fdmWhatDidYouSendReply) repairToInStrings(s.fdmWhatDidYouSendReply);
if (s.fdmWhatCouldISendReply) repairToInStrings(s.fdmWhatCouldISendReply);
if (s.fdmWhySendReply) repairToInStrings(s.fdmWhySendReply);
if (s.fdmAlreadySentReply) repairToInStrings(s.fdmAlreadySentReply);
if (s.fdmPlayerTexts) repairToInStrings(s.fdmPlayerTexts);
if (s.fdmPlayerPhotoSendTexts) repairToInStrings(s.fdmPlayerPhotoSendTexts);
if (s.fdmPlayerGiveNumberTexts) repairToInStrings(s.fdmPlayerGiveNumberTexts);
if (s.fdmChoicePools) repairToInStrings(s.fdmChoicePools);
if (s.fdmMsgPools) repairToInStrings(s.fdmMsgPools);
})();
>>/* =============================================================================
FOTOGRAM – DM TEXT POOLS
=============================================================================
Loaded by: createFotogramDM() & processFotogramDMReply() in phone-fotogram.js
Uses:
setup.fotogramAnonNames → random DM sender name
setup.fotogramDMMessagesByFlag → first DM message (interactive, now in fotogramDMInteractive.twee)
setup.fotogramDMEncouragingMessages → first DM message (non-interactive)
setup.fotogramDMEncouragingReplyMessages → auto-reply after player thanks
setup.fotogramSwapIntroMessages → after number swap intro text
Flag priority: spicy > bikini > risky > hips > ass > boobs > hot > teasing > default
Gender tone: ~70% male / ~30% female
============================================================================= */
/* ── DM sender display names (~100) ──────────────────────────────────────── */
<<set setup.fotogramAnonNames = [
"user_4829",
"anon_7x3k",
"shadow_lurker_99",
"private_account_xx",
"john_d_2947",
"newuser_2024",
"x_curious_x",
"feed_scroller_11",
"mystery_fan_88",
"late_night_owl",
"no_name_just_views",
"ghost_follower_",
"blank_avatar_77",
"just_a_fan_93",
"hidden_profile_x",
"lurker_life_24",
"incognito_mode_on",
"off_the_grid_99",
"sleepless_3am",
"deep_scroll_addict",
"page_stalker_42",
"double_tap_king",
"zoom_master_69",
"guilty_pleasure_x",
"low_key_admirer",
"respectful_viewer",
"taking_my_shot_77",
"nervous_first_dm",
"yolo_sender_99",
"worth_a_try_2024",
"no_regrets_dm",
"heart_on_sleeve_x",
"real_talk_anon",
"honest_fan_88",
"blame_the_post",
"triggered_by_you_",
"helpless_viewer_x",
"down_bad_anon",
"simp_certified_69",
"weak_for_this_feed",
"cant_look_away_24",
"brain_off_mode",
"lost_cause_dm",
"hopeless_but_trying",
"your_biggest_fan_x",
"first_time_sender",
"bold_move_2024",
"courage_mode_on",
"send_it_anon",
"all_in_dm",
"unfiltered_thoughts",
"raw_honesty_77",
"straight_up_fan",
"no_games_here",
"just_saying_xx",
"had_to_say_it",
"couldnt_help_it",
"your_fault_this_time",
"you_did_this_to_me",
"post_made_me_do_it",
"silent_watcher_93",
"quiet_admirer_x",
"dark_stranger_88",
"lone_wolf_dm",
"secret_viewer_24",
"nameless_fan_99",
"faceless_follower",
"distant_crush_x",
"new_here_2024",
"undercover_fan",
"real_quiet_type",
"thirst_trap_victim",
"caught_staring_69",
"random_user_4829",
"anonymous_lurker_",
"private_eye_xx",
"no_chill_anon",
"user_x_unknown",
"fotogram_fan_77",
"scroll_addict_24",
"midnight_lurker_x",
"hidden_flame_88",
"quiet_storm_dm",
"mystery_man_99",
"silent_type_xx",
"anon_viewer_2024",
"no_name_yet_93",
"distant_admirer_x",
"secret_dm_sender",
"late_scroller_69"
]>>
/* ══════════════════════════════════════════════════════════════════════════════
NON-INTERACTIVE DM MESSAGES (low quality / low follower posts)
These are simple one-shot encouraging DMs. Player can only "Reply" once.
══════════════════════════════════════════════════════════════════════════ */
<<set setup.fotogramDMEncouragingMessages = [
"Keep up the good work. Your posts are getting better.",
"Nice progress. Keep posting and building your style.",
"You are doing great. Keep up the consistency.",
"Good shot. Keep it up, you are improving every post.",
"Just wanted to say your feed is getting really good lately.",
"Saw your latest post. You're definitely improving!",
"Cute post! Keep them coming.",
"Hey just a quick note, your content is really growing on me.",
"Not bad at all! You've got potential for sure.",
"Really like the direction your feed is going. Keep posting!",
"You're getting better with every upload, seriously.",
"Nice one! Looking forward to seeing more from you.",
"Your feed is starting to look really polished. Good job!",
"Just followed you. Your recent stuff caught my eye.",
"That post made me hit follow. Keep it up 👍",
"Cool vibe on your page. You're onto something.",
"I see you working on your content. It's paying off.",
"Solid post! You're building something nice here.",
"Your page has a nice aesthetic forming. Keep going!",
"Hey, just wanted to say I enjoy your posts. That's all.",
"You've got a natural eye for photos. Just saying.",
"Your content stands out. Don't stop posting.",
"That was a really nice post. Keep doing your thing!",
"Low key your feed is becoming one of my favorites.",
"Nice work on the last post. Quality is going up!",
"Just a fan stopping by to say keep going 💪",
"Your stuff is underrated honestly. Keep at it.",
"Really enjoyed your latest. You've got a good thing going.",
"Don't stop posting. Your content is getting so much better.",
"Quick message to say your feed brightens my day. That's it."
]>>
<<set setup.fotogramDMEncouragingReplyMessages = [
"Thanks. I'll keep posting.",
"Appreciate it. More coming soon.",
"That means a lot. Really.",
"Thanks for the support. It helps.",
"Hey thanks. I needed to hear that.",
"Quick reply to say I appreciate it.",
"Thanks for saying that. I'll keep at it.",
"That's nice of you. I'll keep posting.",
"Appreciate the kind words. More to come.",
"Thanks. Your support means more than you know.",
"Glad you enjoy it. I won't stop.",
"That's really nice to hear. Thanks.",
"Thanks for following. I won't let you down.",
"Appreciate it. I'm just getting started.",
"Thanks. Posts coming soon.",
"That made my day. I'll keep it up 👍",
"Thanks for the love. More content on the way.",
"Really appreciate that. Keep an eye on my feed.",
"Thanks. I'm trying to get better with each post.",
"Nice of you to say. I'll keep posting.",
"That's sweet. I'll keep doing my thing.",
"Thanks. Feed will only get better from here.",
"Quick reply to say I saw it. Thanks.",
"Glad you like my content. More coming.",
"Thanks for the message. I needed that.",
"Really means a lot. I'll keep going 💪",
"Thanks. Low key your message made my day.",
"Appreciate the follow. I won't disappoint.",
"Thanks. That's all. Just wanted to say it.",
"That's really nice of you. More to come soon."
]>>
/* ══════════════════════════════════════════════════════════════════════════════
SWAP INTRO MESSAGES (after player gives number to anon DM)
Placeholders: {playerName}, {contactName}, {age}
══════════════════════════════════════════════════════════════════════════ */
<<set setup.fotogramSwapIntroMessages = [
"Hey {playerName}! It's {contactName}. I'm {age} and I'm really glad we connected.",
"Yo {playerName}, {contactName} here. I'm {age}. Happy we swapped numbers.",
"Heyy {playerName}! I'm {contactName}, {age}. Nice meeting you for real.",
"Hi {playerName}, it's {contactName}. I'm {age}. Looking forward to talking more.",
"Hey {playerName}! {contactName} here, {age}. I feel like we're gonna vibe.",
"What's up {playerName}? I'm {contactName}, {age}. Glad we can text now.",
"Hey {playerName}, I'm {contactName}. I'm {age}, and I'm excited to get to know you.",
"Hi {playerName}! {contactName} here. {age} years old and already happy we linked up.",
"Hey {playerName}, it's {contactName}. I'm {age}. Can't lie, I'm hyped to chat more.",
"Yo {playerName}! I'm {contactName}, {age}. This is gonna be fun, I can feel it.",
"Hey {playerName} 😊 {contactName}, {age}. So glad we're actually texting now.",
"Sup {playerName}! {contactName} here, {age}. About time we moved off Fotogram haha.",
"Heyyy {playerName}! I'm {contactName}, {age}. Finally we can talk properly.",
"Hi {playerName}! {contactName}, {age}. I was hoping you'd give me your number 😏",
"Hey there {playerName}! {contactName} here. I'm {age}. Let's get to know each other."
]>>Sigma House<<nobr>><<set $location = "galleryArt">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Gallery">>A prestigious gallery showcasing contemporary and classical art. Hushed conversations about the pieces.<</narrative>>
<<showLocationChars "galleryArt">>
<<navMenu>><<navCard "hillcrest">><</navMenu>><<nobr>><<set $location = "gangTerritory">><</nobr>>
<<updateCharacterLocations>>
<<narrative "West Side">>An area controlled by a local gang. Graffiti marks their turf.<</narrative>>
<<showLocationChars "gangTerritory">>
<<navMenu>>
<<navCard "houseCrack">>
<<navCard "wallGraffiti">>
<<navCard "suburbs">>
<</navMenu>><<nobr>><<set $location = "golfClub">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Hillcrest Country Club">>An exclusive country club with pristine greens. Members-only atmosphere and old money connections.<</narrative>>
<<showLocationChars "golfclub">>
<<navMenu>>
<<navCard "golfCourse">>
<<navCard "golfRestaurant">>
<<navCard "golfSpa">>
<<navCard "hillcrest">>
<</navMenu>><<nobr>><<set $location = "golfCourse">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Hillcrest Golf Course">>Eighteen holes of perfectly manicured greens. Caddies attend to wealthy players.<</narrative>>
<<showLocationChars "golfCourse">>
<<navMenu>><<navCard "golfClub">><</navMenu>><<nobr>><<set $location = "golfRestaurant">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Clubhouse">>Fine dining within the country club. Business deals sealed over expensive meals.<</narrative>>
<<showLocationChars "golfRestaurant">>
<<navMenu>><<navCard "golfClub">><</navMenu>><<nobr>><<set $location = "golfSpa">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Spa at Hillcrest">>A luxurious spa attached to the country club. Massages, facials, and relaxation.<</narrative>>
<<showLocationChars "golfSpa">>
<<navMenu>><<navCard "golfClub">><</navMenu>><<nobr>>
<<set $location = "gym">>
<<set $flags = $flags || {}>>
<<if $questState && $questState.active && $questState.active.lily_gym_intro && $questState.active.lily_gym_intro.stage === 0 && !$flags.lilyGymFirstVisit>>
<<set $flags.lilyGymFirstVisit = true>>
<<advanceQuestStage "lily_gym_intro">>
<</if>>
<<updateCharacterLocations>>
<<set _frontDeskQuestActive = ($questState && $questState.active && $questState.active.lily_gym_intro && $questState.active.lily_gym_intro.stage === 1)>>
<<narrative "Iron Works Gym">>A well-equipped fitness center with weights, cardio machines, and training areas.<</narrative>>
<<showLocationChars "gym">>
<<questPrompts>>
<<gymMembershipCheck>>
<<if !_gymMembershipActive && !_frontDeskQuestActive>>
<<narrative>>
You can look around the front area, but the training floor and locker rooms are for members only.
<</narrative>>
<</if>>
<div class="location-actions">
<<if _gymMembershipActive>>
<<gymWorkoutBtn "upper">>
<<gymWorkoutBtn "core">>
<<gymWorkoutBtn "lower">>
<<gymWorkoutBtn "cardio">>
<<else>>
<<if !_frontDeskQuestActive>>
<<if $cashBalance gte 229>>
<<btn "Join (229$)" "gym">>
<<spendCash 229>>
<<if _transactionSuccess>><<gymMembershipStart>><</if>>
<</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked mirror-action-muted" data-tooltip="Not enough money (need 229$)"><i class="icon icon-lock icon-12"></i> Join (229$)</span>
<</if>>
<</if>>
<</if>>
</div>
<<if _gymMembershipActive>>
<<navMenu>>
<<if $corruption gte 3>>
<<navCard "gymMaleLocker">>
<<else>>
<<navCard "gymMaleLocker" "" "" "Requires corruption 3">>
<</if>>
<<navCard "gymFemaleLocker">>
<<navCard "towerA">>
<</navMenu>>
<<else>>
<<navMenu>>
<<navCard "gymMaleLocker" "" "" "Requires an active gym membership">>
<<navCard "gymFemaleLocker" "" "" "Requires an active gym membership">>
<<navCard "towerA">>
<</navMenu>>
<</if>>
<</nobr>><<nobr>>
<<set $location = "gymFemaleLocker">>
<<set $returnAfterToilet = "gymFemaleLocker">>
<<updateCharacterLocations>>
<<gymMembershipCheck>>
<<narrative "Women's Locker Room">>
<<if _gymMembershipActive>>
Lockers line the walls, with a clean vanity mirror and bathroom facilities nearby.
<<else>>
The locker room entrance is reserved for active members.
<</if>>
<</narrative>>
<<if _gymMembershipActive>>
<<discover "GymFemaleLockerWardrobe" "Locker">>
<<showLocationChars "gymFemaleLocker">>
<div class="location-actions">
<<btn "Take a Shower" "useBath">><</btn>>
<<bathroomActions>>
<<mirrorBtn>>
</div>
<</if>>
<<if _gymMembershipActive>>
<<navMenu>>
<<navCard "gymFemaleLockerWardrobe">>
<<navCard "gym">>
<</navMenu>>
<<else>>
<<navMenu>><<navCard "gym">><</navMenu>>
<</if>>
<</nobr>><<nobr>>
<<set $location = "gymFemaleLockerWardrobe">>
<<updateCharacterLocations>>
<<gymMembershipCheck>>
<<narrative "Locker">>
<<if _gymMembershipActive>>
Rows of lockers and a bench give you enough space to change into something else.
<<else>>
This locker is only available with an active gym membership.
<</if>>
<</narrative>>
<</nobr>>
<<if _gymMembershipActive>>
<<wardrobe>>
<<else>>
<<navMenu>><<navCard "gym">><</navMenu>>
<</if>>
<<nobr>><<set $location = "gymMaleLocker">>
<<updateCharacterLocations>>
<<gymMembershipCheck>>
<<set _maleLockerAllowed = (_gymMembershipActive && ($corruption || 0) >= 3)>>
<<narrative "Men's Locker Room">>
<<if _maleLockerAllowed>>
Rows of metal lockers, a bench down the middle, and the faint smell of disinfectant. The showers are through the side door.
<<elseif _gymMembershipActive>>
You stop at the entrance, suddenly aware of the sign on the door and the risk of stepping inside.
<<else>>
The locker room entrance is reserved for active members.
<</if>>
<</narrative>>
<<if _maleLockerAllowed>>
<<showLocationChars "gymMaleLocker">>
<</if>>
<<if _maleLockerAllowed>>
<<navMenu>>
<<navCard "gymMaleLockerShower">>
<<navCard "gym">>
<</navMenu>>
<<else>>
<<navMenu>><<navCard "gym">><</navMenu>>
<</if>>
<</nobr>><<nobr>><<set $location = "gymMaleLockerShower">>
<<updateCharacterLocations>>
<<gymMembershipCheck>>
<<narrative "Men's Showers">>
<<if _gymMembershipActive && ($corruption || 0) >= 3>>
Tiled stalls, chrome fixtures, and steam hanging in the air from the last rush hour.
<<elseif _gymMembershipActive>>
You stop at the entrance. Going into the men's showers feels too bold right now.
<<else>>
The showers are reserved for active gym members.
<</if>>
<</narrative>>
<<if _gymMembershipActive && ($corruption || 0) >= 3>>
<<showLocationChars "gymMaleLockerShower">>
<</if>>
<<navMenu>><<navCard "gymMaleLocker">><</navMenu>>
<</nobr>><<silently>>
<<set $location = "gym">>
<<set $flags = $flags || {}>>
<<if !$characters.gymReceptionNora.firstMet>>
<<set $characters.gymReceptionNora.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>>
<</if>>
<<set $characters.gymReceptionNora.known = true>>
<<logDailyActivity "gymReceptionNora" "talk">>
<<advanceTime 10>>
<<loseStat "energy" 2>>
<<gainStat "mood" 1>>
<<if $questState && $questState.active && $questState.active.lily_gym_intro && $questState.active.lily_gym_intro.stage === 1>>
<<set $flags.lilyGymReceptionTalked = true>>
<<gainCharacterStat "gymReceptionNora" "friendship" 1>>
<<completeQuest "lily_gym_intro">>
<<set _isQuestTalk = true>>
<<else>>
<<set _isQuestTalk = false>>
<</if>>
<<recalculateStats>>
<</silently>>
<<if _isQuestTalk>>
<<narrative "Iron Works Gym">>
Nora looks up from the front desk and gives you a welcoming smile.
<</narrative>>
<<dialog "gymReceptionNora">>Hi, welcome to Iron Works. First time here?<</dialog>>
<<dialog "player">>Yeah, I wanted to say hi and get a quick idea of how things work here.</dialog>>
<<dialog "gymReceptionNora">>Great timing. I'm Nora at the front desk. If you need class info, trainer slots, or locker details, just ask me anytime.</dialog>>
<<narrative>>
After that short chat, the place feels much easier to navigate.
<</narrative>>
<<else>>
<<narrative "Iron Works Gym">>
Nora is organizing sign-up sheets and gives you a quick friendly nod.
<</narrative>>
<<dialog "gymReceptionNora">>Hey. Let me know if you need anything before your workout.<</dialog>>
<</if>>
<div class="location-actions">
<<talkBackButton "gym">>
</div>
<<nobr>>
<<silently>>
<<if ($health || 0) <= 15 and !$hospitalAdmissionInProgress and !$_navigatingBackward>>
<<goto "hospital_admission">>
<</if>>
/* Apply emergency recovery BEFORE advanceTime so we still break loops even if time widgets error out. */
<<set $hunger = 35>>
<<set $thirst = 35>>
<<set $bladder = 35>>
<<set $isSleeping = true>>
<<set $location = "fhBedroom">>
<<advanceTime 180>>
<<set $isSleeping = false>>
<<gainStat "energy" 30>>
/* Prevent immediate re-trigger loop: faint recovery should lift player above critical-faint band. */
<<set _healthNow = parseInt($health, 10) || 0>>
<<if _healthNow <= 20>>
<<set $health = Math.min(100, _healthNow + 8)>>
<</if>>
<<loseStat "stress" 20>>
<<set $appearance.hairCombed = 0>>
<<set _faintVariant = random(1, 3)>>
<<recalculateStats>>
<</silently>>
<</nobr>>
<<if _faintVariant === 1>>
<<narrative "Everything Goes Black">>
Your legs decide first. Then the rest of you.
The floor is cold against your cheek.
<</narrative>>
<<narrative "Three Hours Later">>
Ceiling. Your ceiling. The house is quiet.
You don't remember the stairs.
<</narrative>>
<<dialog "player">>
(Inner voice)
How long was I out?
<</dialog>>
<<narrative>>
Your head hurts. You try to sit up and your arms disagree.
<</narrative>>
<<dialog "player">>
(Inner voice)
Okay. That was stupid of me.
<</dialog>>
<<narrative>>
You sit up. Slowly. The room doesn't spin, which is the best thing that's happened all day.
<</narrative>>
<<elseif _faintVariant === 2>>
<<narrative "The Drop">>
Your knees buckle first. The rest of you follows.
Sound goes out before you hit the floor.
<</narrative>>
<<narrative "A While Later">>
When your eyes open, you recognize your room a second late.
You're on your bed, shoes still on. Blanket thrown across you like someone tried to help in a hurry.
<</narrative>>
<<dialog "player">>
(Inner voice)
I blacked out. Great.
<</dialog>>
<<narrative>>
Your limbs feel heavy. Breathing takes effort.
<</narrative>>
<<dialog "player">>
(Inner voice)
Okay. I can't keep doing this.
<</dialog>>
<<narrative>>
You push yourself upright and wait for the dizziness to pass.
<</narrative>>
<<else>>
<<narrative "No Warning">>
You reach for something to hold and there isn't anything.
Then nothing.
<</narrative>>
<<narrative "Later">>
You wake to pipes cycling in the walls and pressure behind your eyes.
Your room is quiet. Your pulse isn't.
<</narrative>>
<<dialog "player">>
(Inner voice)
Did I really pass out again?
<</dialog>>
<<narrative>>
Your legs feel far away.
<</narrative>>
<<dialog "player">>
(Inner voice)
Enough. This isn't something to shrug off.
<</dialog>>
<<narrative>>
You sit a few seconds before you trust your legs to move.
<</narrative>>
<</if>>
<div class="location-actions">
<<btn "Get Up" "fhBedroom">><</btn>>
</div>
<<nobr>><<set $location = "highSchool">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Lincoln High School">>The local high school with brick buildings and playing fields. Students come and go between classes.<</narrative>>
<<showLocationChars "highSchool">>
<<navMenu>><<navCard "civicCenter">><</navMenu>><<nobr>><<set $location = "hillcrest">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Hillcrest">>The upscale district where the wealthy reside. Manicured lawns, luxury cars, and exclusive establishments.<</narrative>>
<<showLocationChars "hillcrest">>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "galleryArt">>
<<navCard "golfClub">>
<<navCard "streetFifth">>
<<navCard "oldTown">>
<<navCard "universityDistrict">>
<</navMenu>><<nobr>>
<<set $location = "hospital">>
<<if ndef $characters.doctorAdrianCole>><<domInclude "charDoctorAdrianColeHospital">><</if>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set _doctorSeenToday = ($hospitalLastDoctorDayKey || 0) === _dateKey>>
<<updateCharacterLocations>>
<<narrative "St. Luke's Hospital">>
A large medical center with modern facilities. Doctors and nurses rush through the sterile corridors.
<</narrative>>
<<showLocationChars "hospital">>
<div class="location-actions">
<<if ($health || 0) < 100>>
<<if _doctorSeenToday>>
<span class="link-internal btn-style btn-default locked mirror-action-muted" data-tooltip="You already saw the doctor today"><i class="icon icon-lock icon-12"></i> See Doctor (15$)</span>
<<elseif ($cashBalance || 0) gte 15>>
<<btn "See Doctor (15$)" "hospital_see_doctor">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked mirror-action-muted" data-tooltip="Not enough money (need 15$)"><i class="icon icon-lock icon-12"></i> See Doctor (15$)</span>
<</if>>
<<else>>
<span class="link-internal btn-style btn-default locked mirror-action-muted" data-tooltip="Health is already full"><i class="icon icon-lock icon-12"></i> See Doctor (15$)</span>
<</if>>
</div>
<<navMenu>>
<<navCard "civicCenter">>
<</navMenu>>
<</nobr>><<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<<else>>
<<narrative "Everything Goes Dark">>
You barely register your body giving out. One moment you're trying to stay on your feet, the next everything tilts and sound drops away.
When your eyes open again, you're staring at a white hospital ceiling. Your throat is dry, your arms feel heavy, and a monitor beeps steadily beside you.
<</narrative>>
<<vid "assets/content/scenes/oldtown/hospital/hospitalbed.mp4" "100%">>
<<dialog "nurse">>Easy. You're at St. Luke's. You collapsed from severe exhaustion and dehydration. Try to stay calm and don't sit up too fast.<</dialog>>
<<dialog "player">>(Inner voice) I really let it get this far...<</dialog>>
<<nobr>>
<<silently>>
<<set $location = "hospital">>
<<set $hospitalAdmissionInProgress = true>>
<<set _treatmentCost = 150>>
<<set _cashNow = Math.max(0, ($cashBalance || 0))>>
<<set _paidNow = Math.min(_treatmentCost, _cashNow)>>
<<if _paidNow > 0>>
<<set _moneySpendNow = parseFloat($moneySpend) || 0>>
<<set _moneyEarnNow = parseFloat($moneyEarn) || 0>>
<<set $moneySpend = Math.round((_moneySpendNow + _paidNow) * 100) / 100>>
<<set $cashBalance = Math.round((_moneyEarnNow - $moneySpend) * 100) / 100>>
<</if>>
<<set _remainingDebt = Math.max(0, _treatmentCost - _paidNow)>>
<<set _todayKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<if _remainingDebt > 0>>
<<set $hospitalDebt = Math.round((($hospitalDebt || 0) + _remainingDebt) * 100) / 100>>
<<if !$hospitalDebtSinceDateKey || $hospitalDebtSinceDateKey <= 0>>
<<set $hospitalDebtSinceDateKey = _todayKey>>
<</if>>
<<if setup.jobDateAddDays>>
<<set _dueDate = setup.jobDateAddDays($timeSys.year, $timeSys.month, $timeSys.day, 30)>>
<<set $hospitalDebtDueDateKey = (_dueDate.year * 10000) + (_dueDate.month * 100) + _dueDate.day>>
<<else>>
<<set $hospitalDebtDueDateKey = _todayKey + 30>>
<</if>>
<</if>>
<<set $hospitalLastAdmissionDateKey = _todayKey>>
<<set $hospitalAdmissionCount = ($hospitalAdmissionCount || 0) + 1>>
<<set $isSleeping = true>>
<<advanceTime 1440>>
<<set $isSleeping = false>>
<<set $health = 100>>
<<gainStat "energy" 25>>
<<loseStat "stress" 10>>
<<set $appearance.hairCombed = 0>>
<<set $hospitalAdmissionInProgress = false>>
<<set _visitRoll = random(1, 100)>>
<<if _visitRoll <= 20>>
<<set $hospitalVisitCombo = "all">>
<<elseif _visitRoll <= 35>>
<<set $hospitalVisitCombo = "mother_father">>
<<elseif _visitRoll <= 50>>
<<set $hospitalVisitCombo = "mother_brother">>
<<elseif _visitRoll <= 65>>
<<set $hospitalVisitCombo = "father_brother">>
<<elseif _visitRoll <= 77>>
<<set $hospitalVisitCombo = "mother">>
<<elseif _visitRoll <= 89>>
<<set $hospitalVisitCombo = "father">>
<<else>>
<<set $hospitalVisitCombo = "brother">>
<</if>>
<<recalculateStats>>
<</silently>>
<</nobr>>
<<dialog "nurse">>Take a slow breath. You're safe here. Your family is waiting outside and can come in when you're ready.<</dialog>>
<div class="location-actions">
<<btn "Continue" "hospital_family_visit">><</btn>>
</div>
<</if>>
<<nobr>>
<<silently>>
<<set $location = "hospital">>
<<if !$hospitalVisitCombo>><<set $hospitalVisitCombo = "all">><</if>>
<</silently>>
<</nobr>>
<<narrative "St. Luke's Hospital">>
The antiseptic smell hits first. Then the steady beeps, the low hum of machines, and the faint rhythm of footsteps beyond the curtain.
It takes a moment, but your thoughts finally settle into focus. You are not in your room. You are in a hospital bed.
<</narrative>>
<<if $hospitalVisitCombo === "all">>
<<narrative>>
The door opens in a quick burst. Mom steps in first, eyes already glassy from worry. Dad follows with a paper cup in one hand, trying to look composed. Your brother lingers right behind them, quieter than usual.
<</narrative>>
<<dialog "mother">>You're awake. Thank God... You scared us.<</dialog>>
<<dialog "father">>Doctor says you're stable now. That's what matters.<</dialog>>
<<dialog "brother">>I thought someone was joking when they called. Not funny.<</dialog>>
<<dialog "mother">>No more pushing yourself until you collapse, okay?<</dialog>>
<<dialog "father">>One day at a time. You recover first, then we deal with the rest.<</dialog>>
<<dialog "brother">>Next time you feel bad, say it. Don't disappear on us like that.<</dialog>>
<<dialog "player">>I'm sorry. I should've said something earlier.<</dialog>>
<<dialog "player">>I'll slow down. I mean it this time.<</dialog>>
<<elseif $hospitalVisitCombo === "mother_father">>
<<narrative>>
Mom is already by your bed when you open your eyes, fingers curled around the blanket as if letting go would make this less real. Dad stands at the window, then turns as soon as he hears you move.
<</narrative>>
<<dialog "mother">>Sweetheart... I was so worried.<</dialog>>
<<dialog "father">>You're safe now. That's the important part.<</dialog>>
<<dialog "mother">>Please don't ignore your body when it screams for help.<</dialog>>
<<dialog "father">>We'll sort the practical stuff later. Right now, rest.<</dialog>>
<<dialog "player">>I heard you. I pushed too far.<</dialog>>
<<dialog "player">>Thanks for coming... both of you.<</dialog>>
<<elseif $hospitalVisitCombo === "mother_brother">>
<<narrative>>
Mom straightens in her chair the second she notices your eyes open. Your brother, arms crossed in the corner, exhales like he's been holding his breath for hours.
<</narrative>>
<<dialog "mother">>There you are... You had me terrified.<</dialog>>
<<dialog "brother">>You picked the worst way to get a day off, you know that?<</dialog>>
<<dialog "mother">>Not now.<</dialog>>
<<dialog "brother">>Yeah. Sorry. Just... don't do that again.<</dialog>>
<<dialog "mother">>Promise me you'll slow down when you need to.<</dialog>>
<<dialog "player">>Promise. I'm done pretending I'm fine when I'm not.<</dialog>>
<<dialog "player">>And... thank you for staying.<</dialog>>
<<elseif $hospitalVisitCombo === "father_brother">>
<<narrative>>
Dad is seated beside the bed with his elbows on his knees, watchful and still. Your brother paces near the door, then immediately pretends he wasn't pacing when you stir.
<</narrative>>
<<dialog "father">>Good. You're back with us.<</dialog>>
<<dialog "brother">>You look awful. That's... actually a good sign, I guess.<</dialog>>
<<dialog "father">>He means we're relieved.<</dialog>>
<<dialog "brother">>Yeah. That.<</dialog>>
<<dialog "father">>Listen to the doctor. No shortcuts this week.<</dialog>>
<<dialog "player">>No shortcuts. Got it.<</dialog>>
<<dialog "player">>I know I scared you both. Sorry.<</dialog>>
<<elseif $hospitalVisitCombo === "mother">>
<<narrative>>
Mom sits close enough that you can feel the warmth of her hand before you fully wake. The lines of worry around her eyes soften only after she sees you focus on her.
<</narrative>>
<<dialog "mother">>Hi, baby. You gave me the worst scare.<</dialog>>
<<dialog "mother">>I know life's heavy right now, but you don't have to carry all of it alone.<</dialog>>
<<dialog "mother">>Please be gentler with yourself. I need you here.<</dialog>>
<<dialog "player">>I know, Mom. I'm sorry.<</dialog>>
<<dialog "player">>I'll ask for help sooner next time.<</dialog>>
<<elseif $hospitalVisitCombo === "father">>
<<narrative>>
Dad sits in the visitor chair, hands folded, posture rigid in that way he gets when he's worried and trying not to show it.
<</narrative>>
<<dialog "father">>Hey. You're awake.<</dialog>>
<<dialog "father">>The doctor says you'll be fine if you stop running on empty.<</dialog>>
<<dialog "father">>No lecture today. Just recover, then we make a better routine.<</dialog>>
<<dialog "player">>Yeah... I needed that wake-up call.<</dialog>>
<<dialog "player">>Thanks for being here, Dad.<</dialog>>
<<else>>
<<narrative>>
Your brother is the only one in the room when you wake. He looks up from his phone too quickly, like he's been checking every few seconds.
<</narrative>>
<<dialog "brother">>Oh. Finally. Welcome back to planet Earth.<</dialog>>
<<dialog "brother">>Mom and Dad had to handle some stuff, but they kept calling for updates.<</dialog>>
<<dialog "brother">>Just... don't make this your personality, okay? We need you annoying and alive.<</dialog>>
<<dialog "player">>Deal. I'll stay annoying.<</dialog>>
<<dialog "player">>Seriously though... thanks for waiting with me.<</dialog>>
<</if>>
<<dialog "player">>
(Inner voice)
I pushed too far and called it normal. I can't do that again.
<</dialog>>
<<if ($hospitalDebt || 0) > 0>>
<<narrative>>
Before they leave, a nurse places the billing paper on the tray table. Insurance did not cover everything.
Remaining hospital debt: <b>$<<= ($hospitalDebt || 0).toFixed(2)>></b>.
<</narrative>>
<<else>>
<<narrative>>
The paperwork is simple this time. No outstanding balance, no extra forms, no new surprises.
<</narrative>>
<</if>>
<<narrative>>
When the room quiets again, you take one long breath and let it out slowly.
You are still tired. Still shaken. But you are here, and you have another chance to handle things better.
<</narrative>>
<div class="location-actions">
<<btn "Leave Hospital" "hospital">>
/* Discharge reset: full recovery feel, with mood/stress left at mid levels. */
<<set $health = 100>>
<<set $energy = $energyMax || 100>>
<<set $hygiene = 100>>
<<set $hunger = 0>>
<<set $thirst = 0>>
<<set $bladder = 0>>
<<set $arousal = 0>>
<<set $stress = 50>>
<<set $mood = 50>>
<<recalculateStats>>
<</btn>>
</div><<nobr>>
<<set $location = "hotel">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "The Grand Hotel">>
A luxurious hotel with a grand lobby. Crystal chandeliers hang from the ceiling and elegant guests mill about.
<</narrative>>
<<showLocationChars "hotel">>
<<navMenu>>
<<navCard "towerC">>
<</navMenu>><<nobr>><<set $location = "houseCrack">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Condemned House">>A boarded-up house with a dark reputation. Strange sounds at night.<</narrative>>
<<showLocationChars "houseCrack">>
<<navMenu>><<navCard "gangTerritory">><</navMenu>><<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<logDailyActivity "dinerWaitress3" "talk">>
<<set _char = $characters.dinerWaitress3>>
<<set _stats = $characters.dinerWaitress3.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase dishwasherOnBreak: worked >0h but <8h today; dishwasherDone: full 8h done within last 3h */
<<set _phase = "common">>
<<if $job && $job.id === "ruby_dishwasher" && def $jobState>>
<<if $jobState.hoursToday >= 8>>
<<if $jobState.lastShiftEndHour !== null && ($timeSys.hour - $jobState.lastShiftEndHour) <= 3>>
<<set _phase = "dishwasherDone">>
<</if>>
<<elseif $jobState.hoursToday > 0>>
<<set _phase = "dishwasherOnBreak">>
<</if>>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _isDinerBackArea = ($location === "dinerRubysKitchen" || $location === "dinerRubysStorage" || $location === "dinerRubysCorridor" || $location === "dinerRubysBathroom" || $location === "dinerRubysDressingRoom")>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.jakeTopics && setup.jakeTopics[_phase] && setup.jakeTopics[_phase][_levelKey] && setup.jakeTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.jakeTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _ctxKey = "dinerRubys_" + _phase + "_" + _timeSlot>>
<<if setup.jakeTopics && setup.jakeTopics[_phase] && setup.jakeTopics[_phase][_levelKey] && setup.jakeTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.jakeTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.jakeTopics && setup.jakeTopics.common && setup.jakeTopics.common[_levelKey] && setup.jakeTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.jakeTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _fallbackKey = "dinerRubys_common_" + _timeSlot>>
<<if setup.jakeTopics && setup.jakeTopics.common && setup.jakeTopics.common[_levelKey] && setup.jakeTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.jakeTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
/* Pick topic if pool exists */
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "Jake gives you a quick wave as he balances plates on his arms. 'Raincheck!'", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "dinerWaitress3" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "dinerWaitress3" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "dinerWaitress3" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "dinerWaitress3" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerWaitress3.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<logDailyActivity "dinerChef" "talk">>
<<set _char = $characters.dinerChef>>
<<set _stats = $characters.dinerChef.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase dishwasherOnBreak: worked >0h but <8h today; dishwasherDone: full 8h done within last 3h */
<<set _phase = "common">>
<<if $job && $job.id === "ruby_dishwasher" && def $jobState>>
<<if $jobState.hoursToday >= 8>>
<<if $jobState.lastShiftEndHour !== null && ($timeSys.hour - $jobState.lastShiftEndHour) <= 3>>
<<set _phase = "dishwasherDone">>
<</if>>
<<elseif $jobState.hoursToday > 0>>
<<set _phase = "dishwasherOnBreak">>
<</if>>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _isDinerBackArea = ($location === "dinerRubysKitchen" || $location === "dinerRubysStorage" || $location === "dinerRubysCorridor" || $location === "dinerRubysBathroom" || $location === "dinerRubysDressingRoom")>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.jamesTopics && setup.jamesTopics[_phase] && setup.jamesTopics[_phase][_levelKey] && setup.jamesTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.jamesTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _ctxKey = "dinerRubys_" + _phase + "_" + _timeSlot>>
<<if setup.jamesTopics && setup.jamesTopics[_phase] && setup.jamesTopics[_phase][_levelKey] && setup.jamesTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.jamesTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.jamesTopics && setup.jamesTopics.common && setup.jamesTopics.common[_levelKey] && setup.jamesTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.jamesTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _fallbackKey = "dinerRubys_common_" + _timeSlot>>
<<if setup.jamesTopics && setup.jamesTopics.common && setup.jamesTopics.common[_levelKey] && setup.jamesTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.jamesTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
/* Pick topic if pool exists */
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "James stares aggressively at an order ticket and ignores your presence completely.", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "dinerChef" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "dinerChef" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "dinerChef" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "dinerChef" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerChef.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<nobr>><<set $location = "jazzClub">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Blue Note">>An intimate jazz club with live music. Dim lighting and smooth melodies create a sophisticated atmosphere.<</narrative>>
<<showLocationChars "jazzClub">>
<<navMenu>><<navCard "towerC">><</navMenu>><<nobr>>
<<set $location = "dinerRubysManagerOffice">>
<<updateCharacterLocations>>
<<silently>>
<<jobEnsureState>>
<<run State.temporary._promoMeta = (window.getJobPromotionEligibility ? window.getJobPromotionEligibility(State.variables) : { eligible: false, nextTier: null, targetHints: [] })>>
<<set _promoEligible = !!(State.temporary._promoMeta && State.temporary._promoMeta.eligible)>>
<<set _promoNextTier = (State.temporary._promoMeta && State.temporary._promoMeta.nextTier != null) ? State.temporary._promoMeta.nextTier : 0>>
<<set _promoTargets = (State.temporary._promoMeta && State.temporary._promoMeta.targetHints) ? State.temporary._promoMeta.targetHints : []>>
<<set _job = (setup.jobs && setup.jobs[$job.id]) ? setup.jobs[$job.id] : null>>
<<if !_job>>
<<set $job.id = null>>
<<set $job.tier = 1>>
<<set $job.startedOn = null>>
<<jobResetState>>
<<run window.notifyWarning("Your previous job data is invalid and has been reset.")>>
<<goto "dinerRubys">>
<</if>>
<<set _bossName = setup.characterDefs[_job.bossCharId].firstName>>
<<set $jobState.visitedBossThisMonday = true>>
<<set $jobState.bossWantsToSeePlayer = false>>
<<set _reason = $jobState.bossMeetingReason || "">>
<<set _hasAttendanceIssue = !!($jobState.bossMeetingRequired || _reason === "no_show" || _reason === "partial_4_8" || _reason === "partial_6_8" || _reason === "missed_meeting" || _reason === "insufficient_day")>>
<<set _trust = parseInt($jobState.workTrustScore, 10) || 0>>
<<set _band = $jobState.workTrustBand || "mid">>
<<set _dismissAt = (_job.trustPolicy && _job.trustPolicy.dismissalAtTrust != null) ? _job.trustPolicy.dismissalAtTrust : 0>>
<<if ndef $jobState.firingAppealState>><<set $jobState.firingAppealState = "">><</if>>
<<if ndef $jobState.firingLastChanceUsed>><<set $jobState.firingLastChanceUsed = false>><</if>>
<<set _vinceFriendship = (($characters.dinerManager && $characters.dinerManager.stats) ? (parseInt($characters.dinerManager.stats.friendship, 10) || 0) : 0)>>
<<set _vinceFriendshipLevel = (($characters.dinerManager && $characters.dinerManager.stats) ? (parseInt($characters.dinerManager.stats.friendshipLevel, 10) || 0) : 0)>>
<<set _canAskFinalChance = (_job.bossCharId === "dinerManager") && (_vinceFriendshipLevel >= 1) && (_vinceFriendship >= 60) && !$jobState.firingLastChanceUsed>>
<<set _finalChanceLockTip = "" >>
<<if _job.bossCharId !== "dinerManager">>
<<set _finalChanceLockTip = "Only Vince can grant this last chance.">>
<<elseif $jobState.firingLastChanceUsed>>
<<set _finalChanceLockTip = "You already used your one last chance.">>
<<elseif _vinceFriendshipLevel < 1 || _vinceFriendship < 60>>
<<set _finalChanceLockTip = "Requires Vince friendship level 1 and 60.">>
<</if>>
<<if _trust <= _dismissAt>>
<<set $jobState.terminationPending = true>>
<</if>>
<</silently>>
<<if $jobState.terminationPending || _reason === "termination_trust0" || !!$jobState.firingAppealState>>
<<if !$jobState.firingAppealState>>
<<narrative "Manager's Office">>
<<= _bossName>> doesn't look up right away when you enter. The only sound is the scratching of his pen and the hum of the office fan.
After a long moment he closes the file, folds his hands, and finally meets your eyes.
<</narrative>>
<<dialog _job.bossCharId>>
We both knew this meeting was coming. You've burned through every warning I could justify.
<</dialog>>
<<dialog "player">>
Vince, I can explain
<</dialog>>
<<dialog _job.bossCharId>>
No. Don't.
This isn't about one bad day anymore. It's a pattern, and it is costing me staff time and money.
<</dialog>>
<<dialog "player">>
I just need one minute, I
<</dialog>>
<<dialog _job.bossCharId>>
I said no. You're not listening.
<</dialog>>
<<dialog _job.bossCharId>>
I'm taking you off the schedule.
<</dialog>>
<<narrative>>
The words land like a locked door. He pulls a final envelope from the drawer and sets it on the desk between you, already moving on to the next page.
<</narrative>>
<<if _canAskFinalChance>>
<<dialog "player">>
Vince... wait. I know I messed up, but don't end it like this. Give me one last chance and I'll prove I can recover.
<</dialog>>
<<dialog _job.bossCharId>>
One chance. One. If you slip again, we're done and we won't have this talk twice.
<</dialog>>
<<else>>
<<dialog _job.bossCharId>>
No. We're past that point.
<</dialog>>
<</if>>
<div class="btn-center">
<<btn "Accept being fired" "jobBossOfficeCall_ruby">>
<<set $jobState.firingAppealState = "declined">>
<</btn>>
<<if _canAskFinalChance>>
<<btn "Ask for one last chance" "jobBossOfficeCall_ruby">>
<<silently>>
<<set $jobState.firingAppealState = "accepted">>
<<set $jobState.firingLastChanceUsed = true>>
<<set $jobState.workTrustScore = 50>>
<<set $jobState.workTrustBand = "mid">>
<<set $jobState.terminationPending = false>>
<<set $jobState.bossMeetingRequired = false>>
<<set $jobState.bossMeetingReason = "">>
<<set $jobState.bossLieAttemptedThisMeeting = false>>
<<set $jobState.bossLieLastResult = "">>
<<set $jobState.bossLieLastKey = "">>
<<set $jobState.bossLieLastReason = "">>
<<set $jobState.bossLieLastPenaltyAmount = 0>>
<<set $jobState.visitedBossThisMonday = true>>
<<set $jobState.bossWantsToSeePlayer = false>>
<</silently>>
<</btn>>
<<else>>
<<btn "Ask for one last chance" "" "locked" _finalChanceLockTip>><</btn>>
<</if>>
</div>
<<elseif $jobState.firingAppealState === "accepted">>
<<narrative "Manager's Office">>
You step back from the desk with shaky breath, not fired, not safe either.
<</narrative>>
<<dialog _job.bossCharId>>
Your name stays on the roster. That's me taking a risk on you.
<</dialog>>
<<dialog _job.bossCharId>>
Don't make me look stupid for it.
<</dialog>>
<<dialog "player">>
I won't. Thanks... really.
<</dialog>>
<<silently>>
<<set $jobState.firingAppealState = "">>
<<run window.notifySuccess("Vince gave you one final chance. Work trust restored to 50.")>>
<</silently>>
<div class="btn-center">
<<btn "Leave" "dinerRubys">><</btn>>
</div>
<<else>>
<<narrative "Manager's Office">>
He pushes the envelope toward you and waits in silence.
<</narrative>>
<<dialog _job.bossCharId>>
Take care of yourself. This job is over.
<</dialog>>
<<dialog "player">>
...Understood.
<</dialog>>
<<silently>>
<<set $job.id = null>>
<<set $job.tier = 1>>
<<set $job.startedOn = null>>
<<jobResetState>>
<<set $jobState.firingAppealState = "">>
<<run window.notifyWarning("You've been fired from " + _job.workplaceName + " after trust dropped to zero.")>>
<</silently>>
<div class="btn-center">
<<btn "Leave" "dinerRubys">><</btn>>
</div>
<</if>>
<<elseif _promoEligible || ($jobState.promotionPending && ($jobState.pendingTier || 0) > ($job.tier || 1))>>
<<set _nextTier = _promoEligible ? (parseInt($job.tier, 10) + 1) : ($jobState.pendingTier)>>
<<narrative "Manager's Office">>
<<= _bossName>> checks your file and gives a short nod.
<</narrative>>
<<dialog _job.bossCharId>>
You've kept pace. I'm moving you up a tier.
<</dialog>>
<<dialog "player">>
Thank you. I won't waste it.
<</dialog>>
<<silently>>
<<set $job.tier = _nextTier>>
<<set $jobState.promotionPending = false>>
<<set $jobState.pendingTier = 0>>
<<set $jobState.bossMeetingRequired = false>>
<<set $jobState.bossMeetingReason = "">>
<<run window.notifySuccess("Promotion approved. You're now tier " + $job.tier + ".")>>
<</silently>>
<div class="btn-center">
<<btn "Leave" "dinerRubys">><</btn>>
</div>
<<elseif _hasAttendanceIssue>>
<<silently>>
<<set _liePolicy = _job.inPersonLiePolicy || {}>>
<<set _lieOpts = _liePolicy.options || {}>>
<<set _lieAttempted = !!$jobState.bossLieAttemptedThisMeeting>>
<<set _lieResult = $jobState.bossLieLastResult || "">>
<<set _lieKey = $jobState.bossLieLastKey || "">>
<<set _lieSay = (_lieKey && _lieOpts[_lieKey] && _lieOpts[_lieKey].say) ? _lieOpts[_lieKey].say : "">>
<<set _liePenalty = parseFloat($jobState.bossLieLastPenaltyAmount || 0) || 0>>
<<set _reqCorr = (_liePolicy.requiresCorruption != null) ? _liePolicy.requiresCorruption : 2>>
<<set _reqConf = (_liePolicy.requiresConfidence != null) ? _liePolicy.requiresConfidence : 10>>
<<set _hasCorr = (parseInt($corruption, 10) || 0) >= _reqCorr>>
<<set _hasConf = (parseInt($confidence, 10) || 0) >= _reqConf>>
<<set _canLie = _hasCorr && _hasConf && !_lieAttempted>>
<<set _lieLockTip = !_hasCorr ? "Requires Corruption " + _reqCorr : (!_hasConf ? "Requires Confidence " + _reqConf : "")>>
<</silently>>
<<if !_lieAttempted>>
<<narrative "Manager's Office">>
<<= _bossName>> looks up when you knock. He gestures you in.
<</narrative>>
<<dialog _job.bossCharId>>
Sit. We need to talk about your attendance.
<</dialog>>
<<if _reason === "no_show">>
<<dialog _job.bossCharId>>
You missed the whole day. What happened?
<</dialog>>
<<elseif _reason === "partial_4_8" || _reason === "partial_6_8" || _reason === "insufficient_day">>
<<dialog _job.bossCharId>>
You clocked out before the shift was done. Why did you leave early?
<</dialog>>
<<elseif _reason === "missed_meeting">>
<<dialog _job.bossCharId>>
You didn't come to see me when I asked. What's your story?
<</dialog>>
<<else>>
<<dialog _job.bossCharId>>
Your schedule has been inconsistent. Care to explain?
<</dialog>>
<</if>>
<<if _band === "low">>
<<dialog _job.bossCharId>>
You're on thin ice. One more slide and this job is over.
<</dialog>>
<<elseif _band === "high">>
<<dialog _job.bossCharId>>
You've built enough goodwill to recover, but I still want an answer.
<</dialog>>
<</if>>
<div class="btn-center">
<<btn "Take the warning" "jobBossOfficeCall_ruby">>
<<silently>>
<<set $jobState.bossLieAttemptedThisMeeting = true>>
<<set $jobState.bossLieLastResult = "accept">>
<<set $jobState.bossLieLastKey = "">>
<<set $jobState.bossLieLastReason = _reason>>
<<set $jobState.bossLieLastPenaltyAmount = 0>>
<</silently>>
<</btn>>
<<if _canLie>>
<<btn "Try to explain" "jobBossOfficeCall_ruby">>
<<run window.jobRollInPersonLie()>>
<</btn>>
<<else>>
<<btn "Try to explain" "" "locked" _lieLockTip>><</btn>>
<</if>>
</div>
<<else>>
<<if _lieResult === "accept">>
<<dialog "player">>
I understand. It won't happen again.
<</dialog>>
<<dialog _job.bossCharId>>
Good. Go do your job.
<</dialog>>
<<narrative>>
You take the warning on the chin and head for the door.
<</narrative>>
<<elseif _lieResult === "success">>
<<narrative>>
You take a breath and go with it.
<</narrative>>
<<dialog "player">>
<<= _lieSay>>
<</dialog>>
<<dialog _job.bossCharId>>
...Alright. Don't let it happen again. Call next time.
<</dialog>>
<<narrative>>
His shoulders ease. You keep your face neutral and don't say another word.
<</narrative>>
<<dialog "player">>
(Inner voice) Hold it together. Don't smile. Don't celebrate until you're out the door.
<</dialog>>
<<elseif _lieResult === "fail">>
<<narrative>>
You go with the story anyway.
<</narrative>>
<<dialog "player">>
<<= _lieSay>>
<</dialog>>
<<dialog _job.bossCharId>>
Don't.
<</dialog>>
<<dialog "player">>
I
<</dialog>>
<<dialog _job.bossCharId>>
I said don't. I've been running this place longer than you've been working. I can tell.
<</dialog>>
<<narrative>>
He makes a note on the page in front of him without looking up.
<</narrative>>
<<dialog _job.bossCharId>>
That's coming out of your pay. Get back to work.
<</dialog>>
<<dialog "player">>
(Inner voice) He's not asking. He's telling. That was a mistake.
<</dialog>>
<</if>>
<<silently>>
/* Skill tick on any attempt (including "accept" if they at least showed up). Only gain if a lie was rolled. */
<<if _lieResult === "success" || _lieResult === "fail">>
<<set _sg = _liePolicy.skillGainOnAttempt || []>>
<<for _s range _sg>>
<<gainSkill _s.category _s.skill _s.amount>>
<</for>>
<</if>>
/* Notifications (only tangible effects; nothing about lying outcome itself). */
<<if _lieResult === "fail" && _liePenalty > 0>>
<<run window.notifyWarning("Manager logged a deduction of $" + _liePenalty.toFixed(2) + " against this week's pay.")>>
<</if>>
<</silently>>
<div class="btn-center">
<<btn "Leave" "dinerRubys">>
<<silently>>
<<set $jobState.bossMeetingRequired = false>>
<<set $jobState.bossMeetingReason = "">>
<<set $jobState.bossLieAttemptedThisMeeting = false>>
<<set $jobState.bossLieLastResult = "">>
<<set $jobState.bossLieLastKey = "">>
<<set $jobState.bossLieLastReason = "">>
<<set $jobState.bossLieLastPenaltyAmount = 0>>
<<set $jobState.visitedBossThisMonday = true>>
<<set $jobState.bossWantsToSeePlayer = false>>
<</silently>>
<</btn>>
</div>
<</if>>
<<else>>
<<narrative "Manager's Office">>
<<= _bossName>> glances up from his paperwork.
<</narrative>>
<<if _promoTargets.length>>
<<dialog _job.bossCharId>>
You're not ready for promotion yet. I need to see: <<= _promoTargets.join(", ")>>.
<</dialog>>
<<elseif _promoNextTier <= (parseInt($job.tier, 10) || 1)>>
<<dialog _job.bossCharId>>
You're already at the highest tier for this position.
<</dialog>>
<<else>>
<<dialog _job.bossCharId>>
Nothing urgent right now. Keep your performance steady.
<</dialog>>
<</if>>
<div class="btn-center">
<<btn "Leave" "dinerRubys">><</btn>>
</div>
<</if>>
<</nobr>>/* ==========================================
JOB DATABASE (Global)
setup.jobs[jobId] – single job rule, currently only ruby_dishwasher.
Design: 1Developer/JobSystemDesign.md
Economy: 1Developer/economyCheatsheet.md
========================================== */
<<set setup.jobs = {
ruby_dishwasher: {
id: "ruby_dishwasher",
name: "Dishwasher",
workplace: "dinerRubys",
workplaceName: "Ruby's Diner",
position: "Dishwasher",
tierMax: 4,
tierExperienceRequirements: [0, 50, 100, 150],
wageByTier: [3, 4, 5, 6],
xpPerHour: 0.5,
shiftHoursOptions: [2, 4, 6, 8],
requiredHoursPerDay: 8,
tips: false,
/* offWeekdays: 0=Sun … 6=Sat (JS Date). Tue & Thu off → 5 work days. */
schedule: { open: 10, close: 22, offWeekdays: [2, 4] },
payDayWeekday: 1,
minHoursPerWeek: 8,
minHoursWarningsBeforeFire: 3,
bossOfficePassage: "jobBossOfficeCall_ruby",
bossCharId: "dinerManager",
bossMeetingPassage: "jobBossOfficeCall_ruby",
paydayScenePassage: "jobWeeklyPayEnvelope",
trustPolicy: {
base: 60,
fullDayGain: 4,
partial6Penalty: 6,
partial4Penalty: 10,
noShowPenalty: 20,
noShowMessagePenalty: {
none: 20,
honest: 10,
lieSuccess: 8,
lieFail: 16
},
dismissalAtTrust: 0,
discountThresholds: [
{ trust: 85, percent: 10 },
{ trust: 50, percent: 5 }
]
},
liePolicy: {
confidenceBonusMax: 20,
successCapMin: 5,
successCapMax: 95,
options: {
sick: 60,
family: 50,
transport: 40
}
},
excusePolicy: {
cutoffHour: 12
},
/* In-person lie (at boss meeting) requires Corruption 2 + Confidence 10.
Pools:
noshow → for bossMeetingReason "no_show"
partial → for "partial_4_8" / "partial_6_8" / "insufficient_day"
meeting → for "missed_meeting"
Base % is the lie's own difficulty; stats add bonus on top.
Success = partial trust refund (reason-based). Fail = this-week deduction + extra trust drop. */
inPersonLiePolicy: {
requiresCorruption: 2,
requiresConfidence: 10,
persuasionBonusMax: 25,
problemSolvingBonusMax: 15,
confidenceBonusMax: 10,
successCapMin: 5,
successCapMax: 92,
/* Fail: daily pay (wage*8 at current tier) × this pct → added to weeklyDeductions
(same pipe as broken glass, reflected in NEXT weekly envelope). */
failDailyPayPenaltyPct: 50,
failTrustExtraPenalty: 8,
/* Success trust refund by attendance reason */
successTrustRefundByReason: {
no_show: 12,
partial_4_8: 6,
partial_6_8: 4,
insufficient_day: 5,
missed_meeting: 6
},
/* Skill gain on attempt (success or fail) */
skillGainOnAttempt: [
{ category: "social", skill: "persuasion", amount: 1 },
{ category: "mental", skill: "problemSolving", amount: 1 }
],
/* Lie options. pool = which attendance context this lie fits.
base = own chance %, say = player's line. */
options: {
noshow_bus: { pool: "noshow", base: 45, say: "The bus got stuck by the time I figured out another way, it was already too late." },
noshow_sick: { pool: "noshow", base: 55, say: "I woke up sick. I couldn't keep anything down all morning." },
noshow_dad: { pool: "noshow", base: 55, say: "My dad wasn't doing well. I had to stay home with him." },
noshow_mom: { pool: "noshow", base: 52, say: "My mom needed help with something she couldn't handle alone." },
noshow_brother: { pool: "noshow", base: 48, say: "My brother got into trouble at school. I had to deal with it first." },
noshow_mixup: { pool: "noshow", base: 38, say: "I misread the schedule. I thought today was my day off." },
noshow_power: { pool: "noshow", base: 42, say: "The power went out at our place. No alarm I slept right through it." },
noshow_water: { pool: "noshow", base: 45, say: "There was a water problem at the house. I couldn't leave my parents with it." },
noshow_migraine: { pool: "noshow", base: 50, say: "A migraine hit hard this morning. I couldn't even stand up." },
noshow_phone: { pool: "noshow", base: 35, say: "My phone died overnight. No alarm. I only just noticed the time." },
early_mom: { pool: "partial", base: 55, say: "My mom called she needed help with something urgent at home." },
early_brother: { pool: "partial", base: 48, say: "My brother got into something at school. I had to go." },
early_dad: { pool: "partial", base: 55, say: "My dad hurt himself at home. I couldn't stay." },
early_sick: { pool: "partial", base: 58, say: "I started feeling really sick. I didn't want to be around the food like that." },
early_migraine: { pool: "partial", base: 52, say: "A migraine hit hard. I couldn't see straight." },
early_dizzy: { pool: "partial", base: 45, say: "I got lightheaded. I was worried I'd knock something over." },
early_appt: { pool: "partial", base: 42, say: "I had an appointment I completely forgot about until mid-shift." },
early_bus: { pool: "partial", base: 38, say: "The last bus was coming up. I didn't want to get stranded." },
early_period: { pool: "partial", base: 60, say: "Rough cramps. I couldn't push through it." },
early_allergy: { pool: "partial", base: 45, say: "A bad allergic reaction hit. I had to get out before it got worse." },
miss_schedule: { pool: "meeting", base: 40, say: "I was sure our talk was today, not yesterday." },
miss_family: { pool: "meeting", base: 48, say: "Something came up with my family and I lost track of the day." },
miss_nerves: { pool: "meeting", base: 42, say: "I got in my head about it and kept putting it off. I know that's no excuse." }
}
},
/* Tier gates: job XP + dishwashing only; max tier 4 (waitress path uses promotionRules). */
tierProgressionRules: {
2: { jobExperience: 50, skills: { "practical.dishwashing": 30 } },
3: { jobExperience: 100, skills: { "practical.dishwashing": 60 } },
4: { jobExperience: 150, skills: { "practical.dishwashing": 90 } }
},
statCostsPer4Hours: {
energy: -30,
mood: -15,
stress: 20,
hygiene: -30
},
skillGainsPer4Hours: [
{ category: "practical", skill: "dishwashing", amount: 2 }
],
skillGainSoftCap: {
bands: [
{ min: 0, mult: 1.0 },
{ min: 30, mult: 0.6 },
{ min: 60, mult: 0.35 },
{ min: 85, mult: 0.2 }
],
shortShiftMinGainHours: 2,
shortShiftMinGainAmount: 1,
shortShiftMinGainMaxSkill: 35
},
requirements: {
looks: 0,
skills: {},
friendship: {},
flags: []
},
firstWorkDayEventPassage: "dinerFirstdayJob_firstWorkDayEvent",
postWorkPassage: "RubysDishwashWork",
shiftStart: {
clothingCheck: true,
requiredOutfit: { top: "topRubysDishwash", bottom: "bottomRubysDishwash", apron: "apronRubysDishwash" },
statCheck: null
},
promotesTo: "ruby_waitress",
promotionReqs: {
daysWorked: 14,
jobExperience: 100,
looks: 35,
skills: { "social.conversation": 10 },
scenePassage: null
},
promotionRules: {
ruby_waitress: {
daysWorked: 14,
jobExperience: 100,
looks: 35,
confidence: 35,
skills: { "social.conversation": 10 }
}
}
}
/* Later: ruby_waitress, barista, cashier etc. follow same schema. */
}>>
/* ==========================================
WORKPLACE GROUPS
Defines parent-child room relationships for workplaces.
When player is NOT employed at a workplace, characters from
all subLocations are aggregated to mainLocation (except excluded ones).
========================================== */
<<set setup.workplaceGroups = {
dinerRubys: {
mainLocation: "dinerRubys",
subLocations: ["dinerRubysKitchen", "dinerRubysStorage", "dinerRubysManagerOffice"],
excludeFromAggregation: ["dinerManager"]
}
}>><<nobr>>
<<silently>>
/* :passagestart clears $location set again so body background matches workplace */
<<set _returnPassage = $jobWaitReturnTo || "">>
<<if !_returnPassage && $job && $job.id && setup.jobs[$job.id]>>
<<set _returnPassage = setup.jobs[$job.id].workplace>>
<</if>>
<<if !_returnPassage>><<set _returnPassage = "dinerRubys">><</if>>
<<set $location = _returnPassage>>
<<if !$job || !$job.id || !setup.jobs[$job.id]>>
<<goto _returnPassage>>
<</if>>
<<set _jobDef = setup.jobs[$job.id]>>
<<if ndef $jobState.bossMeetingRequired>><<set $jobState.bossMeetingRequired = false>><</if>>
<<if ndef $jobState.weeklyPayPending>><<set $jobState.weeklyPayPending = null>><</if>>
<<if $jobState.weeklyPayPending && !$_navigatingBackward>>
<<set _payPassage = _jobDef.paydayScenePassage || "jobWeeklyPayEnvelope">>
<<goto _payPassage>>
<<elseif ($jobState.bossMeetingRequired || $jobState.bossWantsToSeePlayer || $jobState.terminationPending || $jobState.promotionPending) && !$_navigatingBackward>>
<<set _meetingPassage = _jobDef.bossMeetingPassage || _jobDef.bossOfficePassage || "jobBossOfficeCall_ruby">>
<<goto _meetingPassage>>
<</if>>
<<set _offDays = (_jobDef.schedule && _jobDef.schedule.offWeekdays) || []>>
<<if _offDays.includes($timeSys.weekday)>>
<<if ($jobState.bossMeetingRequired || $jobState.bossWantsToSeePlayer || $jobState.terminationPending || $jobState.promotionPending) && (_jobDef.bossMeetingPassage || _jobDef.bossOfficePassage)>>
<<run window.notifyWarning("No shift today your manager still needs to see you.")>>
<<else>>
<<run window.notifyWarning("No shift today.")>>
<</if>>
<<goto _returnPassage>>
<</if>>
<<set _openH = _jobDef.schedule.open>>
<<set _closeH = _jobDef.schedule.close>>
<<set _openMins = _openH * 60>>
<<set _nowMins = ($timeSys.hour * 60) + ($timeSys.minute || 0)>>
<<set _waitMins = _openMins - _nowMins>>
<<if _waitMins <= 0>>
<<goto _returnPassage>>
<</if>>
/* relax mode: no passive hourly need/energy decay (TimeWidgets / NeedsSystem) */
<<set _waitRelaxBlocks = Math.min(Math.max(_waitMins / 30, 1), 8)>>
<<set _waitStressRelief = Math.min(12, Math.round(3 * _waitRelaxBlocks))>>
<<set _waitMoodLift = Math.min(10, Math.round(2 * _waitRelaxBlocks))>>
<<advanceTime _waitMins "relax">>
<<loseStat "stress" _waitStressRelief>>
<<gainStat "mood" _waitMoodLift>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative "Work">>
You decide to stay and wait right here until shift time.
<</narrative>>
<div class="location-actions">
<<btn "Continue" _returnPassage>><</btn>>
</div>
<</nobr>>
<<nobr>>
<<silently>>
<<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<</if>>
<</silently>>
<<if !$_navigatingBackward>>
<<silently>>
<<if !$job || !$job.id || !setup.jobs[$job.id]>>
<<goto "jobWorkSelect">>
<</if>>
<<set _job = setup.jobs[$job.id]>>
<<set _bossId = _job.bossCharId || "dinerManager">>
<<set _bossName = (setup.characterDefs && setup.characterDefs[_bossId]) ? setup.characterDefs[_bossId].firstName : "your manager">>
<<if ndef $jobState.weeklyPayPending>><<set $jobState.weeklyPayPending = null>><</if>>
<<if !$jobState.weeklyPayPending>>
<<goto "jobWorkSelect">>
<</if>>
<<if _job.workplace>><<set $location = _job.workplace>><</if>>
<<set _beforeLocName = (setup.navCards && $location && setup.navCards[$location]) ? setup.navCards[$location].name : "Before shift">>
<<set _pay = $jobState.weeklyPayPending>>
<<set _gross = Math.max(0, parseFloat(_pay.gross || 0))>>
<<set _dedBase = Math.max(0, parseFloat(_pay.deductionsBase || 0))>>
<<set _disc = parseInt(_pay.discountPercent || 0, 10) || 0>>
<<set _dedFinal = Math.max(0, parseFloat(_pay.deductionsFinal || 0))>>
<<set _net = Math.max(0, parseFloat(_pay.net || 0))>>
/* Chain to next pending job event after collecting pay */
<<set _nextAfterPay = "jobWorkSelect">>
<<set _needsBossAfterPay = ($jobState.bossMeetingRequired || $jobState.bossWantsToSeePlayer || $jobState.terminationPending || $jobState.promotionPending)>>
<<if _needsBossAfterPay>>
<<set _nextAfterPay = _job.bossMeetingPassage || _job.bossOfficePassage || "jobBossOfficeCall_ruby">>
<</if>>
<<set _nextBtnLabel = _needsBossAfterPay ? "Take envelope" : "Take envelope and start shift">>
<</silently>>
<<narrative _beforeLocName>>
<<= _bossName>> steps in front of you with a sealed envelope.
<</narrative>>
<<dialog _bossId>>
This is your weekly pay. Keep your schedule tight and we'll both be happy.
<</dialog>>
<<narrative>>
You open the envelope and quickly check the numbers.
<</narrative>>
<div class="location-actions">
<div><strong>Gross:</strong> $<<= _gross.toFixed(2)>></div>
<div><strong>Deductions:</strong> $<<= _dedBase.toFixed(2)>></div>
<div><strong>Trust discount:</strong> <<= _disc>>%</div>
<div><strong>Final deductions:</strong> $<<= _dedFinal.toFixed(2)>></div>
<div><strong>Net paid:</strong> $<<= _net.toFixed(2)>></div>
</div>
<div class="location-actions">
<<btn _nextBtnLabel _nextAfterPay>>
<<jobFinalizeWeeklyPay>>
<</btn>>
</div>
<</if>>
<</nobr>>/* ========================================
JOB - Execute Shift (from btnPicker)
Receives $selectedDuration in minutes (120, 240, 360, 480)
======================================== */
<div class="divnobr">
<<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<</if>>
<<if !$_navigatingBackward>>
<<jobExecuteShift $selectedDuration "validate">>
<<if $jobShiftSuccess>>
<<silently>>
<<set _job = setup.jobs[$job.id]>>
<<set $location = _job.workplace>>
<<set _postWork = _job.postWorkPassage || _job.workplace || $location>>
<</silently>>
<<if ndef $jobState.firstWorkDayEventShown>><<set $jobState.firstWorkDayEventShown = false>><</if>>
<<if !$jobState.firstWorkDayEventShown>>
<<set $jobState.firstWorkDayEventShown = true>>
<<goto "dinerFirstdayJob_firstWorkDayEvent">>
<<else>>
<<goto _postWork>>
<</if>>
<<else>>
<<set _backPassage = "jobWorkSelect">>
<<narrative "Cannot start shift">>
You couldn't start the shift. Check the notification for details.
<</narrative>>
<div class="location-actions">
<<btn "Back" _backPassage "secondary">><</btn>>
</div>
<</if>>
<</if>>
</div>/* ========================================
JOB - Select Shift Duration
Shows available shift options based on schedule
========================================== */
<<silently>>
<<set _backPassage = previous()>>
<<if !$job || !$job.id>>
<<set _job = null>>
<<else>>
<<set _job = setup.jobs[$job.id]>>
<<if def _job>>
<<set $location = _job.workplace>>
<<if _job.workplace>><<set _backPassage = _job.workplace>><</if>>
<<if !_backPassage || _backPassage === "jobWorkSelect">><<set _backPassage = _job.workplace || "maplewood">><</if>>
<<if ndef $jobState.bossMeetingRequired>><<set $jobState.bossMeetingRequired = false>><</if>>
<<if ndef $jobState.weeklyPayPending>><<set $jobState.weeklyPayPending = null>><</if>>
<<if $jobState.weeklyPayPending && !$_navigatingBackward>>
<<set _payPassage = _job.paydayScenePassage || "jobWeeklyPayEnvelope">>
<<goto _payPassage>>
<<elseif ($jobState.bossMeetingRequired || $jobState.bossWantsToSeePlayer || $jobState.terminationPending || $jobState.promotionPending) && !$_navigatingBackward>>
<<set _meetingPassage = _job.bossMeetingPassage || _job.bossOfficePassage || "jobBossOfficeCall_ruby">>
<<goto _meetingPassage>>
<</if>>
<<jobSetAvailableShifts $job.id>>
<<set _available = $jobAvailableShifts>>
<</if>>
<</if>>
<</silently>>
<<nobr>>
<<if !_job>>
<<narrative "Work">>
You don't have a job here.
<</narrative>>
<div class="location-actions">
<<btn "Back" _backPassage "secondary">><</btn>>
</div>
<<elseif ((_job.schedule && _job.schedule.offWeekdays) || []).includes($timeSys.weekday)>>
<<set _bossSummonOffDay = ($jobState.bossMeetingRequired || $jobState.bossWantsToSeePlayer || $jobState.terminationPending || $jobState.promotionPending) && (_job.bossMeetingPassage || _job.bossOfficePassage)>>
<<if _bossSummonOffDay>>
<<set _bossFirst = setup.characterDefs[_job.bossCharId].firstName>>
<<narrative "Work">>
It's your scheduled day off, no shift today. Still, <<= _bossFirst>> asked you to come in for a talk.
<</narrative>>
<div class="location-actions">
<<set _btnSeeBoss = "See " + _bossFirst>>
<<set _meetingPassage = _job.bossMeetingPassage || _job.bossOfficePassage || "jobBossOfficeCall_ruby">>
<<btn _btnSeeBoss _meetingPassage>><</btn>>
<<btn "Back" _backPassage "secondary">><</btn>>
</div>
<<else>>
<<narrative "Work">>
It's your scheduled day off, no shift today.
<</narrative>>
<div class="location-actions">
<<btn "Back" _backPassage "secondary">><</btn>>
</div>
<</if>>
<<else>>
<<narrative "Work">>
How long do you want to work?
<</narrative>>
<<if ndef _available>><<set _available = []>><</if>>
<<if _available.length === 0>>
<<narrative>><<if $jobWorkNeedsBlock>>You need to eat, drink, or use the bathroom before you can start a shift. Head to the storage room to take care of it.<<else>>You can't start a shift right now - either you've already worked 8 hours today, you don't have enough energy, or the available shift times wouldn't fit before closing.<</if>><</narrative>>
<<else>>
<div class="location-actions">
<<jobWorkPicker "Work" "jobWorkExecute">>
</div>
<</if>>
<div class="location-actions">
<<btn "Back" _backPassage "secondary">><</btn>>
</div>
<</if>>
<</nobr>><<nobr>><<set $location = "laundromat">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Suds Laundromat">>A 24-hour laundromat with flickering lights. The hum of washing machines never stops.<</narrative>>
<<showLocationChars "laundromat">>
<<navMenu>><<navCard "southside">><</navMenu>><<nobr>><<set $location = "lectureHall">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Science Building">>A large auditorium-style classroom. Professors lecture to hundreds of students.<</narrative>>
<<showLocationChars "lectureHall">>
<<navMenu>><<navCard "universityDistrict">><</navMenu>><<nobr>><<set $location = "libraryOldtown">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Carnegie Public Library">>A grand old library with towering bookshelves. The smell of old books and quiet study spaces.<</narrative>>
<<showLocationChars "libraryOldtown">>
<<navMenu>><<navCard "civicCenter">><</navMenu>><<nobr>><<set $location = "libraryUni">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Morrison Library">>A modern library with study spaces and computer labs. Finals week is chaos here.<</narrative>>
<<showLocationChars "libraryUni">>
<<navMenu>><<navCard "universityDistrict">><</navMenu>><<nobr>>
<<set $location = "mall">>
<<updateCharacterLocations>>
<<if ndef $flags>><<set $flags = {}>><</if>>
<<set _goToMallActive = $questState.active.go_to_mall>>
<<set _firstMall = !$flags.firstMallVisit>>
<<set _atMallStage = _goToMallActive && _goToMallActive.stage === 1>>
<<if _firstMall && _atMallStage>>
<<include "quest_go_to_mall_mall_first">>
<<else>>
<<narrative "Metro Shopping Center">>
A massive three-story mall filled with shops, restaurants, and entertainment. Shoppers browse the gleaming storefronts.
<</narrative>>
<<showLocationChars "mall">>
<<questPrompts>>
<<navMenu>>
<<navCard "floorGround">>
<<navCard "floorSecond">>
<<navCard "floorThird">>
<<navCard "downTown">>
<</navMenu>>
<</if>>
<</nobr>><<nobr>>
<<set $location = "mallBathroom">>
<<if ndef $mallBathroomReturn>><<set $mallBathroomReturn = "floorGround">><</if>>
<<set $returnAfterToilet = "mallBathroom">>
<<updateCharacterLocations>>
<<narrative "Restroom">>
A clean, well-maintained restroom. The mall's busy foot traffic keeps it in decent shape – fluorescent lights, neutral tiles, the usual facilities.
<</narrative>>
<div class="location-actions">
<<bathroomActions>>
<<mirrorBtn>>
</div>
<<navMenu>>
<<navCard $mallBathroomReturn>>
<</navMenu>>
<</nobr>>/* ==========================================
EVENT - Mall beauty visit (Something Different arc)
Part 1: mall entry, shoe store, first sting.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "mall">>
<</silently>>
<<narrative "Metro Shopping Center">>
As soon as you step inside the mall, warm air hits your face. Bright lights. Clean glass. People moving past with shopping bags in their hands.
<</narrative>>
<<vid "assets/content/scenes/events/diana/mallEnter.mp4" "100%">>
<<narrative>>
You stop for a second.
<</narrative>>
<<narrative>>
Maybe you'll find a couple of things. Not much. Maybe you'll just look. Maybe you'll see the prices and figure out what's even within reach.
<</narrative>>
<<narrative>>
You start walking.
<</narrative>>
<<narrative>>
The first thing that catches your eye is a shoe store. Through the glass: narrow heels, polished leather, pointed toes. Everything looks like you should already know the price.
<</narrative>>
<<narrative>>
You step inside.
<</narrative>>
<<narrative "Shoe Store">>
The store is quiet. Open. You drift between the shelves.
<</narrative>>
<<vid "assets/content/scenes/events/diana/mallShoeStore.mp4" "100%">>
<<narrative>>
You linger in front of a pair of black heels a little too long, trying to picture them on your feet.
<</narrative>>
<<narrative>>
Then you see the price tag.
<</narrative>>
<<narrative>>
You let go of it a little too quickly.
<</narrative>>
<<narrative>>
Something small goes out inside you.
<</narrative>>
<<narrative>>
You look up.
There are two employees inside. One is behind the register, looking at her phone. The other is fixing boxes on a shelf.
<</narrative>>
<<narrative>>
Neither of them looks at you.
<</narrative>>
<<dialog "player">>
(Inner voice)
Of course.
<</dialog>>
<<narrative "Metro Shopping Center">>
You step back out into the corridor.
<</narrative>>
<<narrative>>
Still crowded. Still bright.
<</narrative>>
<div class="location-actions">
<<btn "Keep walking" "mall_event_beautyVisit_window">><</btn>>
</div>
</div>/* ==========================================
EVENT - Mall beauty visit (Something Different arc)
Part 4: clerk interaction, humiliation, leaving.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "mall">>
<<advanceTime 50>>
<<advanceQuestStage "something_different">>
<</silently>>
<<narrative "Luxury Store">>
You turn.
The woman looks flawless. Hair in place. Makeup light, but exact. She smiles, but her eyes don't.
<</narrative>>
<<dialog "player">>
No, thanks. I was just looking.
<</dialog>>
<<dialog "mallClerk">>
Of course.
<</dialog>>
<<narrative>>
She tilts her head just slightly.
<</narrative>>
<<dialog "mallClerk">>
Looking for anything in particular?
A coat, perhaps?
<</dialog>>
<<dialog "player">>
I don't really know. Just looking.
<</dialog>>
<<narrative>>
She gives the smallest nod, then smooths the sleeve of the pale coat beside her with her fingertips.
<</narrative>>
<<dialog "mallClerk">>
This one just came in.
One of our nicest pieces.
<</dialog>>
<<narrative>>
The same fabric you touched a minute ago.
<</narrative>>
<<dialog "player">>
How much is it?
<</dialog>>
<<narrative>>
She says the number like it means nothing.
<</narrative>>
<<narrative>>
You already know what that number means.
<</narrative>>
<<narrative>>
She sees that too.
<</narrative>>
<<dialog "mallClerk">>
I see…
<</dialog>>
<<narrative>>
Her smile doesn’t change at all.
<</narrative>>
<<dialog "mallClerk">>
If that’s a little out of your range, I’d be happy to show you something more suitable.
<</dialog>>
<<dialog "player">>
I was looking for something more affordable, actually.
<</dialog>>
<<dialog "mallClerk">>
I see.
<</dialog>>
<<narrative>>
This time her eyes move over you more slowly.
<</narrative>>
<<narrative>>
Your shoes.
<</narrative>>
<<narrative>>
Your hands.
<</narrative>>
<<narrative>>
Then they come back to your face and stay there.
<</narrative>>
<<narrative>>
When she smiles again, there's something faintly patronizing in it.
<</narrative>>
<<vid "assets/content/scenes/events/diana/mallLuxuryStoreClerk.mp4" "100%">>
<<dialog "mallClerk">>
This floor does tend to be a bit more… selective.
<</dialog>>
<<narrative>>
She lets the word hang there.
<</narrative>>
<<dialog "mallClerk">>
You might feel more comfortable on the lower levels.
<</dialog>>
<<narrative>>
You swallow, but it doesn’t help.
You don’t meet her eyes anymore.
<</narrative>>
<<narrative>>
You stand there one more frozen second.
<</narrative>>
<<narrative>>
Then you turn and head for the door faster than you meant to.
<</narrative>>
<<narrative>>
The elegant quiet suddenly feels airless.
<</narrative>>
<<dialog "mallClerk">>
(Under her breath, just loud enough.)
Thought so.
<</dialog>>
<<narrative "Metro Shopping Center">>
You hear it.
<</narrative>>
<<narrative>>
You don't turn around.
<</narrative>>
<<narrative>>
You push through the glass door a little too hard and step back into the noise of the mall.
<</narrative>>
<<narrative>>
The beauty shop is right there when you pass it again. This time you don't even look at it.
<</narrative>>
<<narrative>>
For a second, everything blurs.
<</narrative>>
<<narrative>>
Your eyes sting. You want to cry right there in the middle of the corridor - small. Like everyone can see it.
<</narrative>>
<<narrative>>
You hold it in.
<</narrative>>
<<dialog "player">>
(Inner voice)
Forget it.
<</dialog>>
<<narrative>>
You start walking faster.
<</narrative>>
<<narrative>>
Then faster than that.
<</narrative>>
<<narrative>>
You don't look into another store. The second you see the exit, you head straight for it and leave the mall.
<</narrative>>
<div class="location-actions">
<<btn "Leave the mall" "downTown">><</btn>>
</div>
</div>/* ==========================================
EVENT - Mall beauty visit (Something Different arc)
Part 3: entering the luxury store.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "mall">>
<</silently>>
<<narrative "Luxury Store">>
You push the door open and step inside.
<</narrative>>
<<narrative>>
The first thing you notice is the quiet.
<</narrative>>
<<narrative>>
The crowd, the footsteps, the music from the other stores - all of it drops away behind the glass. Everything here moves more slowly.
<</narrative>>
<<narrative>>
The lights are soft. The racks are spaced out. Every piece seems to stand on its own - not like it's untouchable, but like you should already know how to touch it.
<</narrative>>
<<narrative>>
A pale coat on the nearest stand catches your eye. Even from a distance, the fabric looks heavy and smooth.
<</narrative>>
<<vid "assets/content/scenes/events/diana/mallLuxuryStoreTouch.mp4" "100%">>
<<narrative>>
You reach out and brush your fingers over the sleeve.
<</narrative>>
<<narrative>>
Soft.
Actually soft.
<</narrative>>
<<narrative>>
Then you check the tag.
<</narrative>>
<<vid "assets/content/scenes/events/diana/mallLuxuryStoreTag.mp4" "100%">>
<<narrative>>
You keep holding it for a second too long.
<</narrative>>
<<narrative>>
You don't pull your hand back right away. First you look again. Then you realize it really does cost that much.
<</narrative>>
<<dialog "player">>
(Inner voice)
For a coat?
<</dialog>>
<<narrative>>
You move a little farther in.
<</narrative>>
<<narrative>>
A few dresses hang from thin straps nearby. They aren't flashy. They aren't short. But they're the kind that make people look twice without knowing why.
<</narrative>>
<<narrative>>
Your eyes measure one of them. How it would sit at the waist. How it would move when you walked. Whether it would look that calm on you too.
<</narrative>>
<<narrative>>
Then you look at the price.
<</narrative>>
<<narrative>>
This time, you aren't even surprised.
<</narrative>>
<<narrative>>
The store doesn't make you feel small.
<</narrative>>
<<narrative>>
It does something worse.
<</narrative>>
<<narrative>>
Like you skipped ahead to something you weren't ready for.
<</narrative>>
<<narrative>>
You stop in front of another rack. Your fingers brush a metal clasp, then the folded edge of a sleeve.
<</narrative>>
<<narrative>>
Everything is too clean. Like nothing here needs to prove its price.
<</narrative>>
<<narrative>>
And you're still trying to work out what to do with your hands. Where to look. How long to stand still.
<</narrative>>
<<dialog "player">>
(Inner voice)
Just look.
Don't make it obvious.
<</dialog>>
<<dialog "mallClerk">>
Welcome. Can I help you with anything?
<</dialog>>
<div class="location-actions">
<<btn "Turn to her" "mall_event_beautyVisit_clerk">><</btn>>
</div>
</div>/* ==========================================
EVENT - Mall beauty visit (Something Different arc)
Part 2: lingerie window, beauty shop, luxury store draw.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "mall">>
<</silently>>
<<narrative "Metro Shopping Center">>
You keep walking.
<</narrative>>
<<narrative>>
This time it's a lingerie window that slows you down without meaning to. Lace, satin, thin straps... some of it is so small you almost don't want to believe anyone actually wears it.
<</narrative>>
<<narrative>>
One mannequin is dressed in black. The one beside it wears something lighter, softer. They look beautiful. Not comfortable. Beautiful.
<</narrative>>
<<vid "assets/content/scenes/events/diana/mallLingerieWindow.mp4" "100%">>
<<dialog "player">>
(Inner voice)
Do people actually go out wearing this?
Do they just... feel normal in it?
<</dialog>>
<<narrative>>
For a moment your eyes slide away from the display and catch your reflection instead.
<</narrative>>
<<narrative>>
For one second you imagine it on you. How the fabric would sit on your skin. How the strap would fall on your shoulder. How you'd look in a mirror.
<</narrative>>
<<dialog "player">>
(Inner voice)
What would it look like on me?
...I guess we'll never find out.
<</dialog>>
<<narrative>>
You stare a second longer.
<</narrative>>
<<narrative>>
Then you move on.
<</narrative>>
<<narrative>>
A beauty shop catches your eye next. Creams, serums, little polished bottles lined up behind the glass. This was part of why you came here in the first place.
<</narrative>>
<<narrative>>
You slow down.
<</narrative>>
<<dialog "player">>
(Inner voice)
Wait. Let me check these last few stores first.
I'll come back on the way out.
<</dialog>>
<<narrative>>
You weren’t even planning to stop in any of these stores.
<</narrative>>
<<narrative>>
But your feet keep moving anyway.
<</narrative>>
<<narrative>>
That's when a quiet-looking store catches your eye.
<</narrative>>
<<narrative>>
Two long coats hang in the window. A few dresses beside them. They're not loud. Not sparkling. If anything, they're almost too calm.
<</narrative>>
<<narrative>>
And because of that, they look even more expensive.
<</narrative>>
<<narrative>>
You stop.
<</narrative>>
<<narrative>>
Look a little longer.
<</narrative>>
<<narrative>>
Then decide to go inside.
<</narrative>>
<div class="location-actions">
<<btn "Step inside" "mall_event_beautyVisit_luxuryStore">><</btn>>
</div>
</div><<nobr>>
<<silently>>
<<set _ret = $mallWindowShoppingReturn || "floorGround">>
<<requireMinEnergy 15 _ret "Not enough energy.">>
<<set $location = _ret>>
<<updateCharacterLocations>>
<<advanceTime 20 "relax">>
<<loseStat "stress" 3>>
<<gainStat "mood" 6>>
<<loseStat "energy" 5>>
<<recalculateStats>>
<<flushNotifications>>
<<set _windowShopVid = "assets/content/scenes/downtown/mall/windowShop/windowShop" + random(1, 2) + ".mp4">>
<</silently>>
<<narrative "Window Shopping">>
You move slowly along the corridor, letting the displays wash over you lit windows, folded outfits, gadgets you do not need, perfume cards on little sticks. No wallet comes out; it is enough to look, compare, and imagine. The crowd noise turns into a kind of wallpaper, and for a while you are just another face in the flow.
<</narrative>>
<<if _windowShopVid !== "">><<vid _windowShopVid "100%">><</if>>
<div class="location-actions">
<<btn "Continue" _ret>><</btn>>
</div>
<</nobr>><<nobr>>
<<set $location = "mapleChurch">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "St. Mary's Chapel">>
An old stone church with beautiful stained glass windows. The bells chime every hour, a comforting sound for the neighborhood.
<</narrative>>
<<showLocationChars "mapleChurch">>
<<navMenu>>
<<navCard "maplewood">>
<</navMenu>><<nobr>>
<<set $location = "maplewood">>
<<updateCharacterLocations>>
<<silently>>
<<if $flags.firstWorkDayWalkEvent && $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<</if>>
<</silently>>
<<if $flags.firstWorkDayWalkEvent && !$_navigatingBackward>>
<<goto "event_maplewood_firstWorkDayWalkHome">>
<</if>>
<<narrative "Maplewood">>
A quiet residential neighborhood with tree-lined streets and well-kept homes. The scent of freshly cut grass fills the air.
<</narrative>>
<<showLocationChars "maplewood">>
<<questPrompts>>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "FamilyHouse">>
<<navCard "mapleChurch">>
<<navCard "storeCorner">>
<<navCard "sunsetPark">>
<<navCard "downTown">>
<<navCard "oldTown">>
<</navMenu>>
<</nobr>><<nobr>><<set $location = "marina">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Harbor Marina">>Rows of boats and yachts bob in the water. The smell of saltwater and adventure.<</narrative>>
<<showLocationChars "marina">>
<<navMenu>><<navCard "marinaBay">><</navMenu>><<nobr>><<set $location = "marinaBay">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Marina Bay">>The waterfront district with beaches, pier, and seafood restaurants. Salty air and ocean views.<</narrative>>
<<showLocationChars "marinabay">>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "beach">>
<<navCard "beachBar">>
<<navCard "beachClub">>
<<navCard "marina">>
<<navCard "pier">>
<<navCard "restaurantBeach">>
<<navCard "downTown">>
<<navCard "redLightCenter">>
<</navMenu>><<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<logDailyActivity "dinerDishwasher" "talk">>
<<set _char = $characters.dinerDishwasher>>
<<set _stats = $characters.dinerDishwasher.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase dishwasherOnBreak: worked >0h but <8h today; dishwasherDone: full 8h done within last 3h */
<<set _phase = "common">>
<<if $job && $job.id === "ruby_dishwasher" && def $jobState>>
<<if $jobState.hoursToday >= 8>>
<<if $jobState.lastShiftEndHour !== null && ($timeSys.hour - $jobState.lastShiftEndHour) <= 3>>
<<set _phase = "dishwasherDone">>
<</if>>
<<elseif $jobState.hoursToday > 0>>
<<set _phase = "dishwasherOnBreak">>
<</if>>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _isDinerBackArea = ($location === "dinerRubysKitchen" || $location === "dinerRubysStorage" || $location === "dinerRubysCorridor" || $location === "dinerRubysBathroom" || $location === "dinerRubysDressingRoom")>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.mikeTopics && setup.mikeTopics[_phase] && setup.mikeTopics[_phase][_levelKey] && setup.mikeTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.mikeTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _ctxKey = "dinerRubys_" + _phase + "_" + _timeSlot>>
<<if setup.mikeTopics && setup.mikeTopics[_phase] && setup.mikeTopics[_phase][_levelKey] && setup.mikeTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.mikeTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.mikeTopics && setup.mikeTopics.common && setup.mikeTopics.common[_levelKey] && setup.mikeTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.mikeTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _fallbackKey = "dinerRubys_common_" + _timeSlot>>
<<if setup.mikeTopics && setup.mikeTopics.common && setup.mikeTopics.common[_levelKey] && setup.mikeTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.mikeTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
/* Pick topic if pool exists */
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "Mike has his headphones on entirely too loud. You catch a glimpse of suds splashing as he ignores you.", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "dinerDishwasher" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "dinerDishwasher" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "dinerDishwasher" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "dinerDishwasher" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerDishwasher.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<nobr>>
<<set $location = $mirrorReturnPassage>>
<<if ndef $mirrorRoutine>><<set $mirrorRoutine = { makeup: 0, combHair: true, hairCream: true, faceCream: true, dentalCare: true }>><</if>>
<<set _r = $mirrorRoutine>>
<<run State.temporary.applied = []; State.temporary.skipped = [];>>
<<silently>>
/* 1. Comb Hair */
<<if _r.combHair>>
<<if $appearance.hairCombed !== 1>>
<<checkInventoryItem "comb" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<set $appearance.hairCombed = 1>>
<<set $appearance.hairMessiness = 0>>
<<advanceTime 5>>
<<run State.temporary.applied.push("Comb Hair")>>
<<else>>
<<run State.temporary.skipped.push("Comb Hair (no item)")>>
<</if>>
<<else>>
<<run State.temporary.skipped.push("Comb Hair (already done)")>>
<</if>>
<</if>>
/* 2. Hair Cream */
<<if _r.hairCream>>
<<if !$daily.hairCreamUsed>>
<<checkInventoryItem "hair_cream" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<removeFromInventory "hair_cream" 1>>
<<set $weeklyHairCreamUses = ($weeklyHairCreamUses || 0) + 1>>
<<set $daily.hairCreamUsed = true>>
<<set $appearance.hairCare = Math.min(100, ($appearance.hairCare || 0) + 2)>>
<<advanceTime 5>>
<<run State.temporary.applied.push("Hair Cream")>>
<<else>>
<<run State.temporary.skipped.push("Hair Cream (no item)")>>
<</if>>
<<else>>
<<run State.temporary.skipped.push("Hair Cream (already used today)")>>
<</if>>
<</if>>
/* 3. Face Cream */
<<if _r.faceCream>>
<<if !$daily.faceCreamUsed>>
<<checkInventoryItem "face_cream" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<removeFromInventory "face_cream" 1>>
<<set $weeklyFaceCreamUses = ($weeklyFaceCreamUses || 0) + 1>>
<<set $daily.faceCreamUsed = true>>
<<set $appearance.faceCare = Math.min(100, ($appearance.faceCare || 0) + 2)>>
<<advanceTime 5>>
<<run State.temporary.applied.push("Face Cream")>>
<<else>>
<<run State.temporary.skipped.push("Face Cream (no item)")>>
<</if>>
<<else>>
<<run State.temporary.skipped.push("Face Cream (already used today)")>>
<</if>>
<</if>>
/* 4. Dental Care */
<<if _r.dentalCare>>
<<if !$daily.dentalCareUsed>>
<<checkInventoryItem "toothpaste" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<removeFromInventory "toothpaste" 1>>
<<set $weeklyDentalCareUses = ($weeklyDentalCareUses || 0) + 1>>
<<set $daily.dentalCareUsed = true>>
<<set $appearance.dentalCare = Math.min(100, ($appearance.dentalCare || 0) + 2)>>
<<advanceTime 5>>
<<run State.temporary.applied.push("Dental Care")>>
<<else>>
<<run State.temporary.skipped.push("Dental Care (no item)")>>
<</if>>
<<else>>
<<run State.temporary.skipped.push("Dental Care (already used today)")>>
<</if>>
<</if>>
/* 5. Makeup */
<<if _r.makeup >= 1 && _r.makeup <= 5>>
<<set _style = _r.makeup>>
<<set _corr = $corruption || 0>>
<<set _skill = $skills.practical.makeup || 0>>
<<set _corruptUnlock = [0, 1, 3, 4, 5, 5]>>
<<set _skillMin = [0, 0, 20, 40, 75, 75]>>
<<set _canApply = _corr >= _corruptUnlock[_style] && _skill >= _skillMin[_style]>>
<<if _canApply>>
<<checkInventoryItem "makeup_kit" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<set _useKit = "makeup_kit">>
<<else>>
<<checkInventoryItem "portable_makeup" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<set _useKit = "portable_makeup">>
<<else>>
<<set _useKit = "">>
<</if>>
<</if>>
<<if _useKit !== "">>
<<removeFromInventory _useKit 1>>
<<set _quality = Math.min(100, Math.round(50 + _skill * 0.5))>>
<<if ndef $makeup>><<set $makeup = { state: "off", style: 0, quality: 0, lastDecayHourKey: "", lastAppliedHourKey: "", lastDecayMinuteStamp: 0 }>><</if>>
<<set _currHourKey = $timeSys.year + "-" + String($timeSys.month).padStart(2, "0") + "-" + String($timeSys.day).padStart(2, "0") + "-" + String($timeSys.hour).padStart(2, "0")>>
<<run State.temporary._makeupCurrMinuteStamp = Math.floor(Date.UTC($timeSys.year, $timeSys.month - 1, $timeSys.day, $timeSys.hour, $timeSys.minute || 0, 0) / 60000)>>
<<set $makeup.state = "fresh">>
<<set $makeup.style = _style>>
<<set $makeup.quality = _quality>>
<<set $makeup.lastAppliedHourKey = _currHourKey>>
<<set $makeup.lastDecayHourKey = _currHourKey>>
<<set $makeup.lastDecayMinuteStamp = State.temporary._makeupCurrMinuteStamp>>
<<set _cap = 35>>
<<set _gain = 1>>
<<set _newSkill = Math.min(100, Math.min(_skill + _gain, _cap))>>
<<set _actualGain = _newSkill - _skill>>
<<set $skills.practical.makeup = _newSkill>>
<<if _actualGain > 0>>
<<notifySkillChange "makeup" _actualGain>>
<<trackSkillUsage "practical" "makeup">>
<<elseif _skill >= _cap>>
<<run window.notifyWarning("Makeup skill practice is capped at 35. You need a different method to improve further.")>>
<</if>>
<<advanceTime 10>>
<<run State.temporary.applied.push("Makeup")>>
<<else>>
<<run State.temporary.skipped.push("Makeup (no item)")>>
<</if>>
<<else>>
<<run State.temporary.skipped.push("Makeup (corruption/skill)")>>
<</if>>
<</if>>
<</silently>>
<<run State.temporary.appliedStr = (State.temporary.applied || []).join(", "); State.temporary.skippedStr = (State.temporary.skipped || []).join("\n");>>
<<set _appliedStr = State.temporary.appliedStr>>
<<set _skippedStr = State.temporary.skippedStr>>
<<if _appliedStr !== "">>
<<run window.notifySuccess("Routine applied. ")>>
<</if>>
<<if _skippedStr !== "">>
<<run window.notifyWarning("Skipped:\n" + _skippedStr)>>
<</if>>
<<run setTimeout(function () { Engine.play("Mirror", true); }, 0)>>
<</nobr>><<silently>>
<<set $appearance.hairCombed = 1>>
<<set $appearance.hairMessiness = 0>>
<<advanceTime 5>>
<<set _sceneIndex = random(1, 2)>>
<</silently>>
<<narrative "Comb Hair">>
You run the comb through your hair, smoothing out the tangles. Your hair looks neat and tidy now.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/global/mirror/comb/comb1.mp4" "100%">>
<<else>>
<<vid "assets/content/scenes/global/mirror/comb/comb2.mp4" "100%">>
<</if>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div><<nobr>>
<<set _sceneIndex = random(1, 3)>>
<<set $location = $mirrorReturnPassage>>
<<checkInventoryItem "toothpaste" 1>>
<<if !State.temporary.inventoryCheckResult.allowed>>
<<narrative "Mirror">>
You don't have any toothpaste. You need a tube to brush your teeth.
<</narrative>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<<else>>
<<silently>>
<<removeFromInventory "toothpaste" 1>>
<<if !$daily.dentalCareUsed>>
<<set $weeklyDentalCareUses = ($weeklyDentalCareUses || 0) + 1>>
<</if>>
<<set $daily.dentalCareUsed = true>>
<<set $appearance.dentalCare = Math.min(100, ($appearance.dentalCare || 0) + 2)>>
<<advanceTime 5>>
<</silently>>
<<narrative "Dental Care">>
You brush your teeth thoroughly. Your mouth feels fresh and clean. Regular care keeps your teeth and gums healthy.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/global/mirror/dentalcare/dentalCare1.mp4" "100%">>
<<elseif _sceneIndex === 2>>
<<vid "assets/content/scenes/global/mirror/dentalcare/dentalCare2.mp4" "100%">>
<<else>>
<<vid "assets/content/scenes/global/mirror/dentalcare/dentalCare3.mp4" "100%">>
<</if>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<</if>>
<</nobr>><<nobr>>
<<set _sceneIndex = random(1, 2)>>
<<set $location = $mirrorReturnPassage>>
<<checkInventoryItem "face_cream" 1>>
<<if !State.temporary.inventoryCheckResult.allowed>>
<<narrative "Mirror">>
You don't have any face cream. You need a bottle to apply it.
<</narrative>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<<else>>
<<silently>>
<<removeFromInventory "face_cream" 1>>
<<if !$daily.faceCreamUsed>>
<<set $weeklyFaceCreamUses = ($weeklyFaceCreamUses || 0) + 1>>
<</if>>
<<set $daily.faceCreamUsed = true>>
<<set $appearance.faceCare = Math.min(100, ($appearance.faceCare || 0) + 2)>>
<<advanceTime 5>>
<</silently>>
<<narrative "Apply Face Cream">>
You apply the cream to your face, massaging it in gently. It feels refreshing. With regular use, your skin will stay clearer and healthier.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/global/mirror/facecare/faceCream1.mp4" "100%">>
<<else>>
<<vid "assets/content/scenes/global/mirror/facecare/faceCream2.mp4" "100%">>
<</if>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<</if>>
<</nobr>><<nobr>>
<<set _sceneIndex = random(1, 2)>>
<<set $location = $mirrorReturnPassage>>
<<checkInventoryItem "hair_cream" 1>>
<<if !State.temporary.inventoryCheckResult.allowed>>
<<narrative "Mirror">>
You don't have any hair cream. You need a bottle to apply it.
<</narrative>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<<else>>
<<silently>>
<<removeFromInventory "hair_cream" 1>>
<<if !$daily.hairCreamUsed>>
<<set $weeklyHairCreamUses = ($weeklyHairCreamUses || 0) + 1>>
<</if>>
<<set $daily.hairCreamUsed = true>>
<<set $appearance.hairCare = Math.min(100, ($appearance.hairCare || 0) + 2)>>
<<advanceTime 5>>
<</silently>>
<<narrative "Apply Hair Cream">>
You work the cream through your hair, from roots to tips. It feels nourishing. With regular use, your hair will stay healthier.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/global/mirror/haircare/hairCream1.mp4" "100%">>
<<else>>
<<vid "assets/content/scenes/global/mirror/haircare/hairCream2.mp4" "100%">>
<</if>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<</if>>
<</nobr>><<nobr>>
<<goto "Mirror">>
<</nobr>><<nobr>>
<<set _sceneIndex = random(1, 3)>>
<<set $location = $mirrorReturnPassage>>
<<set _style = $makeupChosenStyle || 0>>
<<if _style < 1 || _style > 5>>
<<goto "Mirror">>
<</if>>
<<checkInventoryItem "makeup_kit" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<set _useKit = "makeup_kit">>
<<else>>
<<checkInventoryItem "portable_makeup" 1>>
<<if State.temporary.inventoryCheckResult.allowed>>
<<set _useKit = "portable_makeup">>
<<else>>
<<set _useKit = "">>
<</if>>
<</if>>
<<if _useKit === "">>
<<narrative "Mirror">>
You don't have any makeup. You need a makeup kit or portable makeup.
<</narrative>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<<else>>
<<silently>>
<<removeFromInventory _useKit 1>>
<<set _skill = $skills.practical.makeup || 0>>
<<set _quality = Math.min(100, Math.round(50 + _skill * 0.5))>>
<<if ndef $makeup>><<set $makeup = { state: "off", style: 0, quality: 0, lastDecayHourKey: "", lastAppliedHourKey: "", lastDecayMinuteStamp: 0 }>><</if>>
<<set _currHourKey = $timeSys.year + "-" + String($timeSys.month).padStart(2, "0") + "-" + String($timeSys.day).padStart(2, "0") + "-" + String($timeSys.hour).padStart(2, "0")>>
<<run State.temporary._makeupCurrMinuteStamp = Math.floor(Date.UTC($timeSys.year, $timeSys.month - 1, $timeSys.day, $timeSys.hour, $timeSys.minute || 0, 0) / 60000)>>
<<set $makeup.state = "fresh">>
<<set $makeup.style = _style>>
<<set $makeup.quality = _quality>>
<<set $makeup.lastAppliedHourKey = _currHourKey>>
<<set $makeup.lastDecayHourKey = _currHourKey>>
<<set $makeup.lastDecayMinuteStamp = State.temporary._makeupCurrMinuteStamp>>
<<set _cap = 35>>
<<set _gain = 1>>
<<set _newSkill = Math.min(100, Math.min(_skill + _gain, _cap))>>
<<set _actualGain = _newSkill - _skill>>
<<set $skills.practical.makeup = _newSkill>>
<<if _actualGain > 0>>
<<notifySkillChange "makeup" _actualGain>>
<<trackSkillUsage "practical" "makeup">>
<</if>>
<<advanceTime 10>>
<</silently>>
<<narrative "Apply Makeup">>
You apply your makeup carefully. You went for a <<if _style === 1>>light<<elseif _style === 2>>medium<<elseif _style === 3>>heavy<<elseif _style === 4>>slutty<<else>>bimbo<</if>> look. It looks good.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/global/mirror/makeup/makeup1.mp4" "100%">>
<<elseif _sceneIndex === 2>>
<<vid "assets/content/scenes/global/mirror/makeup/makeup2.mp4" "100%">>
<<else>>
<<vid "assets/content/scenes/global/mirror/makeup/makeup3.mp4" "100%">>
<</if>>
<<if _newSkill >= _cap>>
<<narrative>>
You finish your usual makeup routine, but something feels different. You can no longer feel real progress from repeating the same techniques. If you want to push your makeup skill beyond this point, you'll need a new way to train.
<</narrative>>
<</if>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<</if>>
<</nobr>><<nobr>>
<<set $location = $mirrorReturnPassage>>
<<checkInventoryItem "wet_wipes" 1>>
<<if !State.temporary.inventoryCheckResult.allowed>>
<<narrative "Mirror">>
You don't have any wet wipes. You need them to remove makeup.
<</narrative>>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<<else>>
<<silently>>
<<removeFromInventory "wet_wipes" 1>>
<<if ndef $makeup>><<set $makeup = { state: "off", style: 0, quality: 0, lastDecayHourKey: "", lastAppliedHourKey: "", lastDecayMinuteStamp: 0 }>><</if>>
<<set $makeup.state = "off">>
<<set $makeup.style = 0>>
<<set $makeup.quality = 0>>
<<set $makeup.lastDecayHourKey = "">>
<<set $makeup.lastAppliedHourKey = "">>
<<set $makeup.lastDecayMinuteStamp = 0>>
<<advanceTime 5>>
<</silently>>
<<narrative "Remove Makeup">>
You use the wet wipes to remove your makeup. Your face is clean now.
<</narrative>>
<<vid "assets/content/scenes/global/mirror/removeMakeup.mp4" "100%">>
<div class="location-actions">
<<btn "Back" "Mirror">><</btn>>
</div>
<</if>>
<</nobr>><<nobr>>
<<set $location = $mirrorReturnPassage>>
<<if ndef $mirrorRoutine>><<set $mirrorRoutine = { makeup: 0, combHair: true, hairCream: true, faceCream: true, dentalCare: true }>><</if>>
<<if ndef $mirrorRoutineDraft>>
<<set $mirrorRoutineDraft = { makeup: $mirrorRoutine.makeup, combHair: $mirrorRoutine.combHair, hairCream: $mirrorRoutine.hairCream, faceCream: $mirrorRoutine.faceCream, dentalCare: $mirrorRoutine.dentalCare }>>
<</if>>
<<silently>>
<<set _corr = $corruption || 0>>
<<set _skill = $skills.practical.makeup || 0>>
<<set _makeupOptions = [
{label: "None", value: 0},
{label: "Light", value: 1, disabled: _corr < 1, tooltip: "Need corruption 1"},
{label: "Medium", value: 2, disabled: _corr < 3 or _skill < 20, tooltip: "Need corruption 3 and makeup skill 20"},
{label: "Heavy", value: 3, disabled: _corr < 4 or _skill < 40, tooltip: "Need corruption 4 and makeup skill 40"},
{label: "Slutty", value: 4, disabled: _corr < 5 or _skill < 75, tooltip: "Need corruption 5 and makeup skill 75"},
{label: "Bimbo", value: 5, disabled: _corr < 5 or _skill < 75, tooltip: "Need corruption 5 and makeup skill 75"}
]>>
<</silently>>
<<narrative "Mirror">>
Choose what to include in your daily routine. Confirm to save; Back cancels without saving.
<</narrative>>
<div class="routine-settings-wrap">
<div class="routine-settings">
<div class="routine-row">
<label class="routine-label">Makeup</label>
<span class="routine-control">
<<uiDropdown "$mirrorRoutineDraft.makeup" _makeupOptions>>
</span>
</div>
<div class="routine-row routine-row-checkbox">
<label class="routine-label">Comb Hair</label>
<span class="routine-control"><<checkbox "$mirrorRoutineDraft.combHair" false true autocheck>></span>
</div>
<div class="routine-row routine-row-checkbox">
<label class="routine-label">Hair Cream</label>
<span class="routine-control"><<checkbox "$mirrorRoutineDraft.hairCream" false true autocheck>></span>
</div>
<div class="routine-row routine-row-checkbox">
<label class="routine-label">Dental Care</label>
<span class="routine-control"><<checkbox "$mirrorRoutineDraft.dentalCare" false true autocheck>></span>
</div>
<div class="routine-row routine-row-checkbox">
<label class="routine-label">Face Cream</label>
<span class="routine-control"><<checkbox "$mirrorRoutineDraft.faceCream" false true autocheck>></span>
</div>
</div>
</div>
<div class="routine-note">
If you don't have the required items, those steps won't be applied when you run the routine.
</div>
<div class="location-actions">
<<btn "Confirm" "Mirror" "accent">><<set $mirrorRoutine.makeup = $mirrorRoutineDraft.makeup>><<set $mirrorRoutine.combHair = $mirrorRoutineDraft.combHair>><<set $mirrorRoutine.hairCream = $mirrorRoutineDraft.hairCream>><<set $mirrorRoutine.faceCream = $mirrorRoutineDraft.faceCream>><<set $mirrorRoutine.dentalCare = $mirrorRoutineDraft.dentalCare>><<set $mirrorRoutineConfirmed = true>><<run window.notifySuccess("Routine saved")>><</btn>>
<<btn "Back" "Mirror">><</btn>>
</div>
<</nobr>><<nobr>>
<<if ndef $mirrorRoutine>><<set $mirrorRoutine = { makeup: 0, combHair: true, hairCream: true, faceCream: true, dentalCare: true }>><</if>>
<<set $mirrorRoutineDraft = { makeup: $mirrorRoutine.makeup, combHair: $mirrorRoutine.combHair, hairCream: $mirrorRoutine.hairCream, faceCream: $mirrorRoutine.faceCream, dentalCare: $mirrorRoutine.dentalCare }>>
<<goto "mirrorRoutineSettings">>
<</nobr>><<nobr>><<set $location = "motelRedLight">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Stardust">>A seedy motel with hourly rates. Curtains always drawn.<</narrative>>
<<showLocationChars "motelRedLight">>
<<navMenu>><<navCard "redLightCenter">><</navMenu>><<nobr>><<set $location = "motelSuburbs">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Route 9 Motor Lodge">>A cheap motel with hourly rates. No questions asked.<</narrative>>
<<showLocationChars "motelSuburbs">>
<<navMenu>><<navCard "suburbs">><</navMenu>><<silently>>
<<set $location = "fhBackyard">>
<<logDailyActivity "mother" "talk">>
<<set _stats = $characters.mother.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Context key Backyard has no evening slot, fallback to afternoon */
<<set _contextKey = "fhBackyard_" + _timeSlot>>
<<set _levelPool = setup.motherTopics["level" + _level]>>
<<set _context = (_levelPool && _levelPool[_contextKey]) ? _levelPool[_contextKey] : null>>
/* Fallback: backyard has morning + afternoon only */
<<if !_context && _levelPool>>
<<if _timeSlot === "evening">>
<<set _context = _levelPool["fhBackyard_afternoon"]>>
<<else>>
<<set _context = _levelPool["fhBackyard_morning"]>>
<</if>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time & stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
<<if _topic.friendship>><<gainCharacterStat "mother" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "mother" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "mother" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.mother.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhKitchen">>
<<logDailyActivity "mother" "talk">>
<<set _stats = $characters.mother.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Context key */
<<set _contextKey = "fhKitchen_" + _timeSlot>>
<<set _levelPool = setup.motherTopics["level" + _level]>>
<<set _context = (_levelPool && _levelPool[_contextKey]) ? _levelPool[_contextKey] : null>>
/* Fallback: morning if current slot has no content */
<<if !_context && _levelPool>>
<<set _context = _levelPool["fhKitchen_morning"]>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time & stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
<<if _topic.friendship>><<gainCharacterStat "mother" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "mother" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "mother" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.mother.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhLivingroom">>
<<logDailyActivity "mother" "talk">>
<<set _stats = $characters.mother.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Context key */
<<set _contextKey = "fhLivingroom_" + _timeSlot>>
<<set _levelPool = setup.motherTopics["level" + _level]>>
<<set _context = (_levelPool && _levelPool[_contextKey]) ? _levelPool[_contextKey] : null>>
/* Fallback */
<<if !_context && _levelPool>>
<<set _context = _levelPool["fhLivingroom_morning"]>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time & stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
<<if _topic.friendship>><<gainCharacterStat "mother" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "mother" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "mother" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.mother.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<silently>>
<<set $location = "fhParentsRoom">>
<<logDailyActivity "mother" "talk">>
<<set _stats = $characters.mother.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
/* Context key ParentsRoom has morning + evening only, no afternoon */
<<set _contextKey = "fhParentsRoom_" + _timeSlot>>
<<set _levelPool = setup.motherTopics["level" + _level]>>
<<set _context = (_levelPool && _levelPool[_contextKey]) ? _levelPool[_contextKey] : null>>
/* Fallback: afternoon → evening */
<<if !_context && _levelPool>>
<<if _timeSlot === "afternoon">>
<<set _context = _levelPool["fhParentsRoom_evening"]>>
<<else>>
<<set _context = _levelPool["fhParentsRoom_morning"]>>
<</if>>
<</if>>
/* Pick random topic → random variation */
<<set _topicKeys = Object.keys(_context)>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
/* Time & stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 3>>
<<flushNotifications>>
<<if _topic.friendship>><<gainCharacterStat "mother" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "mother" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "mother" "love" _topic.love>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.mother.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<div class="prologue-content-area">
<div class="prologue-header">
<div class="prologue-age-badge">Present Day</div>
<h1 class="prologue-title">Home</h1>
<div class="prologue-header-line"></div>
</div>
<<image "assets/content/locations/maplewood/familyHouse/fhLivingroom.webp" "100%">>
<<narrative "Inside">>
You step through the front door. Everyone does.
<</narrative>>
<<narrative>>
Dad turns the key, the door swings open. Mom enters first, then dad, you, your brother trailing behind.
<</narrative>>
<<dialog "mother">>
(stopping in the entryway, taking it in) Wow... It's really done.
<</dialog>>
<<dialog "father">>
(setting down his bag) Not bad. Better than I thought it'd look.
<</dialog>>
<<narrative>>
The living room is bigger than the old one. Your couch is against the wrong wall. Mom keeps walking past it and then walking back, like she hasn't decided either.
<</narrative>>
<<narrative>>
Through the window, you can see the street. Different trees. Different cars. Different sounds.
<</narrative>>
<<dialog "mother">>
(heading for the kitchen) I need to check the fridge. Put away what we bought on the road.
<</dialog>>
<<dialog "brother">>
(looking around, antsy) Where are the rooms?
<</dialog>>
<<dialog "father">>
Upstairs. Left side is yours, right side is your sister's.
<</dialog>>
<<narrative>>
Your brother nods and heads for the stairs immediately. Dad turns back toward the door to get the remaining bags.
<</narrative>>
<<narrative>>
You pause in the living room, alone for a moment.
<</narrative>>
<<narrative>>
This is it. This is home now.
<</narrative>>
<<narrative>>
The house smells like paint. The old house smelled like mom's cooking and whatever dad was fixing that week. Here, nothing yet.
<</narrative>>
<<image "assets/content/locations/maplewood/familyHouse/fhDownstairs.webp" "100%">>
<<narrative>>
You walk to the stairs. Wooden steps, shiny railing. Everything looks barely lived in.
<</narrative>>
<<narrative>>
From below you hear mom ask dad where the coffee box is. He doesn't answer the first time.
<</narrative>>
<<narrative>>
You start climbing.
<</narrative>>
<<narrative>>
Each step feels significant. Like crossing a threshold. Leaving something behind. Moving toward something unknown.
<</narrative>>
<<narrative>>
At the top, the hallway stretches out. Cream walls, still empty. No pictures yet, no memories marked on these walls.
<</narrative>>
<<narrative>>
Your brother's already in his roomdoor half-open, music playing faintly.
<</narrative>>
<<narrative>>
Your room is at the end of the hall. On the right.
<</narrative>>
<<narrative>>
The door is slightly ajar.
<</narrative>>
<<image "assets/content/locations/maplewood/familyHouse/fhBedroom.webp" "100%">>
<<narrative>>
You can see insidebed made, desk positioned by the window, your closet already installed. Dad must have arranged everything before you arrived.
<</narrative>>
<<narrative>>
You push the door open wider and step inside.
<</narrative>>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-primary">Enter Your Room</span>' "prologueBedroom">><</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<!-- Sidebar - All Completed -->
<div class="prologue-sidebar">
<div class="prologue-sidebar-title">Your Journey</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Early Years (0-5)</div>
<<if $prologueSelections.early>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.early.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.early.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Childhood (6-9)</div>
<<if $prologueSelections.childhood>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.childhood.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.childhood.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Formative (10-12)</div>
<<if $prologueSelections.formative>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.formative.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.formative.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Adolescent (13-15)</div>
<<if $prologueSelections.adolescent>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.adolescent.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.adolescent.effect</div>
</div></div>
<</if>>
</div>
<div class="prologue-sidebar-section">
<div class="prologue-sidebar-section-title completed">Coming of Age (16-17)</div>
<<if $prologueSelections.coming>>
<div class="prologue-sidebar-item"><div>
<div class="prologue-sidebar-item-text">$prologueSelections.coming.title</div>
<div class="prologue-sidebar-item-effect">$prologueSelections.coming.effect</div>
</div></div>
<</if>>
</div>
</div>
<div class="prologue-content-area">
<div class="prologue-progress-container">
<div class="prologue-progress-bar">
<div class="prologue-progress-fill" style="width: 100%;"></div>
</div>
<div class="prologue-progress-steps">
<span class="prologue-progress-step completed">0-5</span>
<span class="prologue-progress-step completed">6-9</span>
<span class="prologue-progress-step completed">10-12</span>
<span class="prologue-progress-step completed">13-15</span>
<span class="prologue-progress-step completed">16-17</span>
</div>
</div>
<div class="prologue-header">
<div class="prologue-age-badge">Present Day</div>
<h1 class="prologue-title">Arrival</h1>
<div class="prologue-header-line"></div>
</div>
<<narrative>>
The car slows down. You open your eyes.
<</narrative>>
<<narrative>>
The memories fade back into the past where they belong. You're here now. In the present. In the city.
<</narrative>>
<<image "assets/content/scenes/prologue/arrivalCarPOV.webp" "100%">>
<<narrative>>
Buildings rise higher than anything back home. Glass and steel catching the afternoon sun. Traffic flows in three lanes, constant and humming.
<</narrative>>
<<narrative>>
A siren wails somewhere in the distance. A truck horn blares. The sounds blend into a rhythm you don't recognize yet.
<</narrative>>
<<narrative>>
People walk past. So many people, more than you'd see in a week back home. A woman in a business suit talks on her phone, heels clicking against the pavement. Two teenagers laugh, bumping shoulders as they walk. An old man sits on a bench, feeding pigeons.
<</narrative>>
<<image "assets/content/scenes/prologue/citySidewalks.webp" "100%">>
<<narrative>>
Dad navigates through the streets, checking his phone for directions. Mom looks out the window, taking it all in.
<</narrative>>
<<dialog "mother">>
It's bigger than I thought. (says quietly)
<</dialog>>
<<narrative>>
Your brother presses his face against the window.
<</narrative>>
<<dialog "brother">>
Look at that building! It must be like, fifty floors!
<</dialog>>
<<narrative>>
You watch it all unfold. The energy here is different. Everything faster, everyone a stranger. Back home, you knew every face.
<</narrative>>
<<narrative>>
But there's something else too. Something electric. Possibility. The feeling that anything could happen here.
<</narrative>>
<<narrative>>
Dad turns onto a quieter street. Trees line the sidewalk. The buildings are smaller here, older, more residential. The city noise fades to a distant hum.
<</narrative>>
<<dialog "father">>
This is it. (pulling up to a house)
<</dialog>>
<<image "assets/content/locations/maplewood/familyHouse.webp" "100%">>
<<dialog "mother">>
New beginnings. (squeezes your hand)
<</dialog>>
<<narrative>>
Your brother leans forward.
<</narrative>>
<<dialog "brother">>
We're here?
<</dialog>>
<<narrative>>
You open the car door. It smells like exhaust and somebody's dryer vent. You're not sure why that feels promising.
<</narrative>>
<<narrative>>
Everything that shaped you brought you here.
<</narrative>>
<<narrative>>
And now, your real life begins.
<</narrative>>
<div class="prologue-summary-grid">
<div class="prologue-summary-card">
<div class="prologue-summary-card-header">Early Development</div>
<div class="prologue-summary-card-body">
<<if $prologueSelections.early>>
<div class="prologue-summary-item">
<span class="prologue-summary-label">$prologueSelections.early.title</span>
<span class="prologue-summary-value">$prologueSelections.early.effect</span>
</div>
<</if>>
<<if $prologueSelections.childhood>>
<div class="prologue-summary-item">
<span class="prologue-summary-label">$prologueSelections.childhood.title</span>
<span class="prologue-summary-value">$prologueSelections.childhood.effect</span>
</div>
<</if>>
<<if $prologueSelections.formative>>
<div class="prologue-summary-item">
<span class="prologue-summary-label">$prologueSelections.formative.title</span>
<span class="prologue-summary-value">$prologueSelections.formative.effect</span>
</div>
<</if>>
</div>
</div>
<div class="prologue-summary-card">
<div class="prologue-summary-card-header">Who You Became</div>
<div class="prologue-summary-card-body">
<<if $prologueSelections.adolescent>>
<div class="prologue-summary-item">
<span class="prologue-summary-label">$prologueSelections.adolescent.title</span>
<span class="prologue-summary-value">$prologueSelections.adolescent.effect</span>
</div>
<</if>>
<<if $prologueSelections.coming>>
<div class="prologue-summary-item">
<span class="prologue-summary-label">$prologueSelections.coming.title</span>
<span class="prologue-summary-value">$prologueSelections.coming.effect</span>
</div>
<</if>>
</div>
</div>
</div>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-secondary">Back</span>' "comingofAge">><</link>>
<<link '<span class="prologue-btn prologue-btn-primary">Step Inside</span>' "newHome">>
/* Apply prologue bonuses to multipliers */
<<if $prologueSelections.early && $prologueSelections.early.stat>>
<<set _stat = $prologueSelections.early.stat>>
<<set _val = $prologueSelections.early.value>>
<<if $statMultipliers[_stat] !== undefined>>
<<set $statMultipliers[_stat] *= _val>>
<<elseif $skillMultipliers[_stat] !== undefined>>
<<set $skillMultipliers[_stat] *= _val>>
<</if>>
<</if>>
<<if $prologueSelections.childhood && $prologueSelections.childhood.stat>>
<<set _stat = $prologueSelections.childhood.stat>>
<<set _val = $prologueSelections.childhood.value>>
<<if $statMultipliers[_stat] !== undefined>>
<<set $statMultipliers[_stat] *= _val>>
<<elseif $skillMultipliers[_stat] !== undefined>>
<<set $skillMultipliers[_stat] *= _val>>
<</if>>
<</if>>
<<if $prologueSelections.formative && $prologueSelections.formative.stat>>
<<set _stat = $prologueSelections.formative.stat>>
<<set _val = $prologueSelections.formative.value>>
<<if $statMultipliers[_stat] !== undefined>>
<<set $statMultipliers[_stat] *= _val>>
<<elseif $skillMultipliers[_stat] !== undefined>>
<<set $skillMultipliers[_stat] *= _val>>
<</if>>
<</if>>
<<if $prologueSelections.adolescent && $prologueSelections.adolescent.stat>>
<<set _stat = $prologueSelections.adolescent.stat>>
<<set _val = $prologueSelections.adolescent.value>>
<<if $statMultipliers[_stat] !== undefined>>
<<set $statMultipliers[_stat] *= _val>>
<<elseif $skillMultipliers[_stat] !== undefined>>
<<set $skillMultipliers[_stat] *= _val>>
<</if>>
<</if>>
<<if $prologueSelections.coming && $prologueSelections.coming.stat>>
<<set _stat = $prologueSelections.coming.stat>>
<<set _val = $prologueSelections.coming.value>>
<<if $statMultipliers[_stat] !== undefined>>
<<set $statMultipliers[_stat] *= _val>>
<<elseif $skillMultipliers[_stat] !== undefined>>
<<set $skillMultipliers[_stat] *= _val>>
<</if>>
<</if>>
<</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<div class="prologue-content-area">
<div class="prologue-header">
<div class="prologue-age-badge">Next Day</div>
<h1 class="prologue-title">Morning</h1>
<div class="prologue-header-line"></div>
</div>
<<narrative "Morning">>
Sunlight hits your face. You open your eyes.
<</narrative>>
<<narrative>>
You wake up and don't recognize the ceiling. Then you do.
<</narrative>>
<<narrative>>
New house.
<</narrative>>
<<narrative>>
You sit up. The room looks smaller in daylight. Also less hostile.
<</narrative>>
<<narrative>>
You get out of bed, stretch. Through the window, you can see the street coming to life. People leaving for work, a jogger passing by, a dog walker.
<</narrative>>
<<narrative>>
Life. Movement. The city waking up.
<</narrative>>
<<narrative>>
You take a quick shower, get dressed, head downstairs.
<</narrative>>
<<image "assets/content/locations/maplewood/familyHouse/fhKitchen.webp" "100%">>
<<narrative>>
The smell of coffee greets you. Mom's already up, moving around the kitchen. Dad sits at the table, reading something on his phone. Your brother's there too, still half-asleep, picking at a piece of toast.
<</narrative>>
<<dialog "mother">>
(brightly) Morning. How'd you sleep?
<</dialog>>
<<dialog "player">>
Okay.
<</dialog>>
<<narrative>>
She pours you coffee without asking. Remembers how you like it.
<</narrative>>
<<narrative>>
You sit down. Same table, new kitchen. It's going to take a while.
<</narrative>>
<<dialog "father">>
(looking up from his phone) Morning. Ready for today?
<</dialog>>
<<dialog "player">>
(shrugging) I guess.
<</dialog>>
<<narrative>>
Breakfast is quiet. Comfortable quiet, not awkward.
<</narrative>>
<<dialog "father">>
(checking his watch) Alright, we need to head out soon.
<</dialog>>
<<dialog "brother">>
(groaning) Do I have to?
<</dialog>>
<<dialog "father">>
(firmly) Yes. Registration can't wait.
<</dialog>>
<<narrative>>
They finish eating, stand up. Dad grabs his keys from the counter.
<</narrative>>
<<dialog "father">>
We'll be back by 3 or 4. Depends on traffic.
<</dialog>>
<<dialog "mother">>
Drive safe.
<</dialog>>
<<narrative>>
Dad pauses by your chair.
<</narrative>>
<<dialog "father">>
You okay here?
<</dialog>>
<<dialog "player">>
Yeah.
<</dialog>>
<<dialog "father">>
Good.
<</dialog>>
<<narrative>>
He ruffles your hair like he used to when you were little.
<</narrative>>
<<dialog "father">>
We'll be back soon.
<</dialog>>
<<narrative>>
Your brother waves vaguely, already halfway out the door.
<</narrative>>
<<narrative>>
Then they're gone.
<</narrative>>
<<narrative>>
The house feels emptier suddenly. Just you and mom.
<</narrative>>
<<dialog "mother">>
(clearing breakfast dishes) Want to help me organize a bit?
<</dialog>>
<<dialog "player">>
Sure.
<</dialog>>
<<narrative "Time Passes">>
You spend the next few hours moving boxes, arranging things, making the house feel more like home. It's mundane work but somehow comforting. Normal.
<</narrative>>
<<narrative>>
Mom talks while she worksabout the neighborhood, the grocery store she found online, plans for the week. You half-listen, half-focus on the task at hand.
<</narrative>>
<<narrative "15:22">>
Then she checks her phone.
<</narrative>>
<<dialog "mother">>
Oh, it's already past 3. Time flies. (She looks at you) Actually, honey, can you do me a favor?
<</dialog>>
<<dialog "player">>
What?
<</dialog>>
<<dialog "mother">>
Run to the corner shop? We need milk and bread.
<</dialog>>
<<narrative>>
She pulls out her wallet, hands you a twenty.
<</narrative>>
<<dialog "mother">>
This should be enough. Keep the change.
<</dialog>>
<<dialog "player">>
(taking the money) Okay.
<</dialog>>
<<narrative>>
She glances at your clothes.
<</narrative>>
<<dialog "mother">>
And... maybe change first? You're going out.
<</dialog>>
<<narrative>>
Right. You're still in the comfortable clothes you threw on this morning.
<</narrative>>
<<dialog "player">>
Okay.
<</dialog>>
<<narrative>>
You head upstairs to your room.
<</narrative>>
<<narrative>>
Time to get dressed properly.
<</narrative>>
<<narrative>>
Time to step outside for the first time.
<</narrative>>
<<narrative>>
Time to see what this new city really looks like.
<</narrative>>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-primary">Go to your room</span>' "fhBedroom">>
<<set $timeSys.year = 2025>>
<<set $timeSys.month = 8>> /* August */
<<set $timeSys.day = 15>> /* Mid-August */
<<set $timeSys.weekday = 4>> /* Friday */
<<set $timeSys.hour = 15>> /* 3 PM */
<<set $timeSys.minute = 22>> /* 3:22 PM */
/* Ensure wardrobe is properly initialized */
<<if !$wardrobe or !$wardrobe.owned or $wardrobe.owned.length === 0>>
<<set $wardrobe = {
owned: ["tshirt_white_owned", "tshirt_blue_owned", "tshirt_gray_owned", "jeans_blue_owned", "leggings_black_owned", "dress_summer_owned", "socks_white_owned", "socks_black_owned", "sneakers_flat_owned", "sneakers_girly_owned", "bra_sport_white_owned", "bra_black_owned", "panties_white_owned", "panties_pink_owned", "pajama_color_owned"],
outfits: [null, null, null, null, null],
equipped: {
top: "tshirt_white_owned",
bottom: "jeans_blue_owned",
shoes: "sneakers_flat_owned",
socks: "socks_white_owned",
panty: "panties_white_owned",
bra: "bra_sport_white_owned"
}
}>>
<</if>>
/* Re-enable UI */
<<set $hideTopbarNav = false>>
<<set $hideTopbarTimebox = false>>
<<set $hideTopbarNotifications = false>>
<<set $hideRightbar = false>>
<<set $hideTopbar = false>>
<<set $discoveredStoreCorner = true>>
<<run $('body').removeClass('fullscreen-centered')>>
/* Update character locations */
<<updateCharacterLocations>>
/* Mark prologue as complete (for quest requirements) */
<<set $prologue_complete = true>>
/* Appearance: worst values at prologue end; hairCare 50, hairMessiness 0; after sleep = messy (hairCombed 0) */
<<set $appearance.hairCare = 50>>
<<set $appearance.hairMessiness = 0>>
<<set $appearance.hairCombed = 0>>
<<set $appearance.faceCare = 0>>
<<set $appearance.dentalCare = 0>>
<<recalculateStats>>
/* Ensure map is hidden until Marcus gives it */
<<set $hideRightbarMap = true>>
/* Mother gives money for groceries */
<<earnMoney 20>>
/* Start the first quest (Quest System V2) */
<<startQuest "first_shopping">>
<</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>><<nobr>>
<<set $location = "nightClub">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "Pulse Nightclub">>
Pulsing music and flashing lights fill this popular club. The dance floor is packed with people having a good time.
<</narrative>>
<<showLocationChars "nightclub">>
<<navMenu>>
<<navCard "downTown">>
<</navMenu>><<nobr>><<set $location = "oldFactory">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Abandoned Textile Mill">>An abandoned factory with broken windows. Rumors of illegal activity swirl around.<</narrative>>
<<showLocationChars "oldFactory">>
<<navMenu>><<navCard "southside">><</navMenu>><<nobr>>
<<set $location = "oldTown">>
<<updateCharacterLocations>>
<<narrative "Old Town">>The historic heart of the city. Cobblestone streets, vintage shops, and a sense of nostalgia.<</narrative>>
<<showLocationChars "oldtown">>
<<questPrompts>>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "shopCoffeeOldtown">>
<<navCard "storeBook">>
<<navCard "pharmacy">>
<<navCard "dinerRubys">>
<<navCard "shopBarber">>
<<navCard "storeHardware">>
<<navCard "civicCenter">>
<<navCard "maplewood">>
<<navCard "downTown">>
<<navCard "hillcrest">>
<<navCard "universityDistrict">>
<</navMenu>>
<</nobr>><<nobr>>
<<updateCharacterLocations>>
<<if ndef $flags>><<set $flags = {}>><</if>>
<<set _alreadyDone = $flags.parkBenchFirstEncounter>>
<<set _lilyAtPark = $characters.parkRunnerLily && $characters.parkRunnerLily.currentLocation === "sunsetPark">>
<<set _triggerFirst = !_alreadyDone && _lilyAtPark>>
<<if _triggerFirst>>
<<include "parkBench_firstEncounter">>
<<else>>
<<include "parkBench_rest">>
<</if>>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "sunsetPark">>
<<if ndef $flags>>
<<set $flags = {}>>
<</if>>
<<set $flags.parkBenchFirstEncounter = true>>
<<if ndef $characters.parkRunnerLily>>
<<set $characters.parkRunnerLily = { stats: { love: 0, friendship: 0, lust: 0, trust: 0 }, firstMet: null, known: false, currentLocation: null, currentStatus: null }>>
<</if>>
<<set $characters.parkRunnerLily.known = true>>
<<if !$characters.parkRunnerLily.firstMet>>
<<set $characters.parkRunnerLily.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>>
<</if>>
<<gainCharacterStat "parkRunnerLily" "friendship" 2>>
<<set $selectedDuration = parseInt($selectedDuration) || 30>>
<<set _restScale = $selectedDuration / 30>>
<<advanceTime $selectedDuration>>
<<loseStat "stress" Math.max(1, Math.round(5 * _restScale))>>
<<gainStat "mood" Math.max(1, Math.round(5 * _restScale))>>
<<gainStat "energy" Math.max(1, Math.round(4 * _restScale))>>
<<flushNotifications>>
<<recalculateStats>>
<<set _vidNum = random(1, 2)>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/sittingBenchPark" + _vidNum + ".mp4">>
<</silently>>
<<narrative "Park Bench">>
You settle onto the bench, letting out a quiet breath. The park stretches out in front of you – the pond, the trees, the steady flow of joggers and walkers.
A moment later you notice her. A woman in athletic wear – light gray leggings, a fitted top, running shoes – is crouched beside the bench, one foot up on the edge of the seat as she ties her shoelace. Her hair is pulled back in a ponytail. She finishes the knot, pulls her foot down, and catches you looking.
You hadn't meant to stare. You quickly look away, then back – an awkward half-smile. //Sorry.//
<</narrative>>
<<vid "assets/content/scenes/maplewood/park/bench/lilyBenchInteraction.mp4" "100%">>
<div class="talk-interaction">
<<dialog "player">>
Sorry, I didn't mean to disturb you.
<</dialog>>
<<dialog "parkRunnerLily">>
Oh, no worries at all! You're fine. I was just fixing my laces – one came loose on my last lap.
<</dialog>>
<<narrative>>
She straightens up and brushes her hands on her thighs. A friendly smile. She doesn't seem bothered. If anything, she looks like she might be open to talking.
<</narrative>>
<<dialog "player">>
Actually… I've been meaning to ask. I'm still new around here and I've been looking for sportswear – you know, something decent for jogging or yoga. I can't seem to find anything in the neighborhood.
<</dialog>>
<<dialog "parkRunnerLily">>
Ah, I know exactly what you mean! There's not much around Maplewood for that. I got these from the mall – Metro Shopping Center, downtown. You take the bus or drive toward the city center, and once you're in Downtown the building's hard to miss. Big glass front, three floors. The sports store's on the ground level, near the main entrance. Decent prices, decent selection. It's a bit of a trip, but worth it if you're serious about working out.
<</dialog>>
<<dialog "player">>
That helps, thanks. I'll have to check it out.
<</dialog>>
<<dialog "parkRunnerLily">>
You're welcome! Oh – I'm Lily, by the way. I run here a few times a week. Maybe I'll see you around.
<</dialog>>
<<narrative>>
She gives a small wave, warm but not pushy.
<</narrative>>
<<dialog "player">>
Nice to meet you. I'm <<print $player.firstName>>. Yeah, maybe you will.
<</dialog>>
<<dialog "parkRunnerLily">>
Good luck with the sportswear hunt. And welcome to the neighborhood!
<</dialog>>
<<narrative>>
She stretches her arms overhead, then starts a light jog back toward the path. You watch her go – ponytail swinging, steady rhythm – before settling back against the bench.
<</narrative>>
</div>
<<silently>>
<<startQuest "go_to_mall">>
<<discover "DownTown" "Downtown">>
<<discover "Mall" "Metro Shopping Center">>
<</silently>>
<<vid _benchVid "100%">>
<div class="location-actions">
<<btn "Stand Up" $activityOrigin>><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "sunsetPark">>
<<set $selectedDuration = parseInt($selectedDuration) || 30>>
<<set _restScale = $selectedDuration / 30>>
<<set _stressLoss = Math.max(1, Math.round(10 * _restScale))>>
<<set _moodGain = Math.max(1, Math.round(5 * _restScale))>>
<<set _energyGain = Math.max(1, Math.round(5 * _restScale))>>
<<set _stressBefore = $stress || 0>>
<<set _moodBefore = $mood || 0>>
<<set _energyBefore = $energy || 0>>
<<set $stress = Math.max(0, _stressBefore - _stressLoss)>>
<<set _actualStressLoss = _stressBefore - $stress>>
<<if _actualStressLoss > 0>><<queueStatChange "stress" (-1 * _actualStressLoss)>><</if>>
<<set $mood = Math.min(100, _moodBefore + _moodGain)>>
<<set _actualMoodGain = $mood - _moodBefore>>
<<if _actualMoodGain > 0>><<queueStatChange "mood" _actualMoodGain>><</if>>
<<set $energy = Math.min($energyMax || 100, _energyBefore + _energyGain)>>
<<set _actualEnergyGain = $energy - _energyBefore>>
<<if _actualEnergyGain > 0>><<queueStatChange "energy" _actualEnergyGain>><</if>>
<<advanceTime $selectedDuration "relax">>
<<flushNotifications>>
<<recalculateStats>>
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 and _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 and _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _sceneMode = random(1, 2)>>
<<if _sceneMode is 1>>
<<set _baseRoll = random(1, 2)>>
<<if _baseRoll is 1>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/sittingBenchPark1.mp4">>
<<set _benchText = "You find a quiet bench overlooking the pond and settle in. People pass by - joggers, dog walkers, families with children. The park has its own rhythm. Sometimes it's nice to just sit and watch life go by.">>
<<else>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/sittingBenchPark2.mp4">>
<<set _benchText = "You take a seat and let yourself go still for a while. The sounds of the park fill in around you - distant footsteps, a dog barking somewhere, leaves shifting in the breeze.">>
<</if>>
<<else>>
<<if _timeSlot is "morning">>
<<set _sceneRoll = random(1, 2)>>
<<if _sceneRoll is 1>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/morningScene1.mp4">>
<<set _benchText = "A couple sits close on a nearby bench, laughing softly at something between them. The morning is still quiet enough that their warmth feels like part of the atmosphere.">>
<<else>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/morningScene2.mp4">>
<<set _benchText = "Not far away, a man leans in toward the woman beside him, talking in a low voice with a grin that says he's trying his luck. She looks amused. You look away, smiling to yourself.">>
<</if>>
<<elseif _timeSlot is "afternoon">>
<<set _sceneRoll = random(1, 2)>>
<<if _sceneRoll is 1>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/afternoonScene1.mp4">>
<<set _benchText = "A couple on the grass nearby pulls each other close for a kiss, completely at ease in the middle of the busy afternoon park. You glance over and then back to the pond.">>
<<else>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/afternoonScene2.mp4">>
<<set _benchText = "Two people hold each other on the grass, not saying much. Around them the park hums with midday activity, but they seem content in their own quiet corner of it.">>
<</if>>
<<else>>
<<set _sceneRoll = random(1, 2)>>
<<if _sceneRoll is 1>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/eveningScene1.mp4">>
<<set _benchText = "An older couple sits together on the bench across the path, leaning into each other without a word. They look like people who stopped needing to explain themselves a long time ago.">>
<<else>>
<<set _benchVid = "assets/content/scenes/maplewood/park/bench/eveningScene2.mp4">>
<<set _benchText = "A couple nearby sits close, arms around each other as the evening light softens everything. The park is winding down and the moment feels unhurried and easy.">>
<</if>>
<</if>>
<</if>>
<</silently>>
<<narrative "Park Bench">>
<<print _benchText>>
<</narrative>>
<<vid _benchVid "100%">>
<div class="location-actions">
<<btn "Stand Up" $activityOrigin>><</btn>>
</div>
<</nobr>><<silently>>
<<set _parkActRet = $activityOrigin || "sunsetPark">>
<<requireMinEnergy 25 _parkActRet "Not enough energy.">>
/* Set location from activity origin */
<<set $location = $activityOrigin>>
/* Run the jogging activity */
<<set $daily.jogDone = true>>
<<advanceTime 45 "relax">>
<<loseStat "energy" 15>>
<<loseStat "stress" 15>>
<<gainStat "cardio" 1 20>>
<<gainStat "lowerBody" 1 10>>
<<gainSkill "physical" "running" 1 25>>
<<flushNotifications>>
<<recalculateStats>>
/* Calorie burn if tracking */
<<if $gameSettings.trackCalories>>
<<set $dailyCalorieIntake -= 300>>
<</if>>
/* Text variations */
<<set _variation = random(1, 3)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "You start jogging along the park's running path. The fresh air fills your lungs as you find your rhythm. Other joggers pass by with a nod. By the time you finish, you're sweating but energized.">>
<<case 2>>
<<set _sceneText = "The path is perfect for running - smooth and shaded. You push yourself harder today, trying to beat your usual pace. Your legs burn but you keep going. When you finally slow to a walk, you feel accomplished.">>
<<case 3>>
<<set _sceneText = "You put in your earbuds and start your jog. The music helps you maintain a steady pace as you loop around the park. People watching, breathing fresh air, moving your body - this is exactly what you needed.">>
<</switch>>
/* Random jog video: jogPark1.mp4 – jogPark4.mp4 */
<<set _vidNum = random(1, 4)>>
<<set _jogVid = "assets/content/scenes/maplewood/park/jog/jogPark" + _vidNum + ".mp4">>
<</silently>>
<<narrative "Park Jogging">>
<<print _sceneText>>
<</narrative>>
<<vid _jogVid "100%">>
<div class="location-actions">
<<btn "Finish" $location>><</btn>>
</div><<silently>>
<<set $location = $activityOrigin || "sunsetPark">>
<<set $selectedDuration = parseInt($selectedDuration) || 30>>
<<set _restScale = $selectedDuration / 30>>
<<set _stressLoss = Math.max(1, Math.round(10 * _restScale))>>
<<set _moodGain = Math.max(1, Math.round(5 * _restScale))>>
<<set _energyGain = Math.max(1, Math.round(5 * _restScale))>>
<<set _stressBefore = $stress || 0>>
<<set _moodBefore = $mood || 0>>
<<set _energyBefore = $energy || 0>>
<<set $stress = Math.max(0, _stressBefore - _stressLoss)>>
<<set _actualStressLoss = _stressBefore - $stress>>
<<if _actualStressLoss > 0>><<queueStatChange "stress" (-1 * _actualStressLoss)>><</if>>
<<set $mood = Math.min(100, _moodBefore + _moodGain)>>
<<set _actualMoodGain = $mood - _moodBefore>>
<<if _actualMoodGain > 0>><<queueStatChange "mood" _actualMoodGain>><</if>>
<<set $energy = Math.min($energyMax || 100, _energyBefore + _energyGain)>>
<<set _actualEnergyGain = $energy - _energyBefore>>
<<if _actualEnergyGain > 0>><<queueStatChange "energy" _actualEnergyGain>><</if>>
<<advanceTime $selectedDuration "relax">>
<<flushNotifications>>
<<recalculateStats>>
<<run $(document).trigger(':passagerender')>>
<<set _vidNum = random(1, 2)>>
<<set _restVid = "assets/content/scenes/maplewood/park/rest/restingPark" + _vidNum + ".mp4">>
<</silently>>
<<narrative "Park Rest">>
You find a quiet spot under a tree and sit back. No rush, no planjust the shade and the sound of leaves. You let your thoughts drift. After a while you feel a bit more settled.
<</narrative>>
<<vid _restVid "100%">>
<div class="location-actions">
<<btn "Continue" $location>><</btn>>
</div><<silently>>
<<set $flags = $flags || {}>>
<<set $flags.lilyGymQuestOffered = true>>
<<startQuest "lily_gym_intro">>
<<discover "DownTown" "Downtown">>
<<discover "Skyline" "Skyline">>
<<discover "TowerA" "Apex Tower">>
<<discover "Gym" "Iron Works Gym">>
<<gainCharacterStat "parkRunnerLily" "friendship" 2>>
<<gainCharacterStat "parkRunnerLily" "trust" 1>>
<<gainCharacterStat "parkRunnerLily" "love" 1>>
<<gainCharacterStat "parkRunnerLily" "lust" 1>>
<</silently>>
<<narrative "Sunset Park">>
The morning air is still cool, your breath visible in short puffs. Lily slows her pace beside you, her ponytail swinging as she drops into a walk. A bead of sweat traces down her collarbone. She turns toward you, breathing just a little heavy.
<</narrative>>
<<dialog "parkRunnerLily">>You've really been looking good lately, you know.<</dialog>>
<<dialog "parkRunnerLily">>I noticed it right away. When we first started running together, your body looked a bit softer... more loose.<</dialog>>
<<dialog "parkRunnerLily">>But now? Look at those hips... they've really tightened up nicely. It looks amazing on you.<</dialog>>
<<narrative>>
You weren't used to someone studying you like that. Shame, maybe. Or something else. You didn't want to look too closely at which one it was.
<</narrative>>
<<dialog "player">>Thanks... I didn't think it was that noticeable.<</dialog>>
<<dialog "parkRunnerLily">>Oh it's noticeable, trust me. (winking)<</dialog>>
<<dialog "parkRunnerLily">>At this rate, I might start running behind you just to enjoy the view better.<</dialog>>
<<dialog "player">>Can you not say stuff like that out loud... (face turning red, looking away)<</dialog>>
<<dialog "parkRunnerLily">>What? I'm just being honest! (laughing softly)<</dialog>>
<<dialog "parkRunnerLily">>Have you ever done any weight training at a real gym before?<</dialog>>
<<dialog "player">>Not really. I've only been running outside like this.<</dialog>>
<<dialog "parkRunnerLily">>Then you should try Iron Works Gym downtown. Trainers actually know what they're doing - not like those commercial chains.<</dialog>>
<<dialog "parkRunnerLily">>I go there a few times a week. I'm getting stronger...<</dialog>>
<<narrative>>
For just a second, her eyes drifted somewhere past you, like she was remembering someone.
<</narrative>>
<<dialog "parkRunnerLily">>...and the view isn't bad either.<</dialog>>
<<dialog "player">>The view? What do you mean?<</dialog>>
<<dialog "parkRunnerLily">>Haha, well... obviously the hot, muscular guys. The place is full of them - all sexy and well-built. It's quite the motivation, honestly.<</dialog>>
<<narrative>>
Something tightened in your stomach. You didn't try to figure out what it was.
<</narrative>>
<<dialog "player">>Wait, seriously? That many good-looking guys?<</dialog>>
<<dialog "player">>I don't usually go to gyms... but now you've got me a little curious.<</dialog>>
<<dialog "parkRunnerLily">>You should come check it out sometime. Maybe we'll even run into each other there.<</dialog>>
<<dialog "parkRunnerLily">>Would be fun to see you sweating for a change... instead of me chasing you. (smirking)<</dialog>>
<<narrative>>
She held your gaze for a second longer - the smirk still there, but something else behind it. Then she turned and picked up her pace again, leaving you standing in the cool morning air.
<</narrative>>
<div class="location-actions">
<<set _currentCharLocation = $characters.parkRunnerLily.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
<<silently>>
<<set $location = "sunsetPark">>
<<set $flags = $flags || {}>>
<<set $flags.lilyJogInviteSeen = true>>
<<set $flags.lilyJogUnlocked = true>>
<<advanceTime 10>>
<<loseStat "energy" 3>>
<<gainStat "mood" 2>>
<<gainCharacterStat "parkRunnerLily" "friendship" 1>>
<<recalculateStats>>
<</silently>>
<<narrative "Sunset Park">>Lily stops stretching and straightens up when she sees you. She's lightly flushed, her sportswear sitting on her with that effortless ease as always.<</narrative>>
<<dialog "parkRunnerLily">>Hey, hold on a sec. Just thought of something.<</dialog>>
<<dialog "player">>What's up?<</dialog>>
<<dialog "parkRunnerLily">>I see you out here sometimes, I'm out here too... why aren't we running together yet?<</dialog>>
<<dialog "parkRunnerLily">>Next time you see me here, no excuses. We're running together.<</dialog>>
<<dialog "player">>Together? I can't keep up with your pace.<</dialog>>
<<dialog "parkRunnerLily">>Sure you can, sure you can. A good run would do you some good anyway.<</dialog>>
<<dialog "player">>Hold on, what's that supposed to mean?<</dialog>>
<<narrative>>Lily shrugged and gave you an innocent little smile - though there was something not quite innocent behind her eyes.<</narrative>>
<<dialog "parkRunnerLily">>Nothing, nothing. Just saying.<</dialog>>
<<dialog "parkRunnerLily">>I'm serious though. You're young, you don't think about it - but one day your body's just gonna give up on you, girl.<</dialog>>
<<dialog "parkRunnerLily">>I'm warning you now so you can't sit there later going "I wish I'd listened to Lily."<</dialog>>
<<dialog "parkRunnerLily">>So? Deal?<</dialog>>
<<narrative>>Lily held out her hand, waiting for the shake.<</narrative>>
<<dialog "player">>As if I could say no.<</dialog>>
<<narrative>>You took her hand. She squeezed it firmly - exactly the way you'd expect her to.<</narrative>>
<<dialog "parkRunnerLily">>That's my girl.<</dialog>>
<<narrative>>Without even realizing it, you found yourself smiling back at her.<</narrative>>
<<narrative>>Now you can jog with Lily when you see her in the park.<</narrative>>
<div class="location-actions">
<<set _currentCharLocation = $characters.parkRunnerLily.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div><<nobr>>
<<silently>>
<<set $flags = $flags || {}>>
<<set _isFirstMiaMeet = false>>
<<set $flags.lilyJogMiaMeetTriggered = true>>
<<if $characters.neighborMia>>
<<set $characters.neighborMia.known = true>>
<<if !$characters.neighborMia.firstMet>>
<<set $characters.neighborMia.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>>
<<set _isFirstMiaMeet = true>>
<</if>>
<<set $characters.neighborMia.currentLocation = "maplewood">>
<<gainCharacterStat "neighborMia" "friendship" 1>>
<</if>>
<<gainCharacterStat "parkRunnerLily" "friendship" 1>>
<<if _isFirstMiaMeet && window.notifySuccess>>
<<run window.notifySuccess("New relation added: Mia (Harrisons)")>>
<</if>>
<</silently>>
<<narrative "Sunset Park">>You slowed down near Sunset Park's exit, your breath still coming in short bursts. Lily stood beside you, hands on her hips, cheeks flushed red as she laughed.<</narrative>>
<<narrative>>A girl was standing by the curb, glancing at her phone and then up the street every few seconds. Her long blonde hair moved lightly in the wind. A black crop top and tight leggings hugged her frame.<</narrative>>
<<dialog "parkRunnerLily">>Mia!<</dialog>>
<<narrative>>She looked up. Her blue eyes landed on Lily first, then on you. She paused for a second. The corner of her lips curled slowly.<</narrative>>
<<dialog "neighborMia">>Oh my God, Lily!<</dialog>>
<<dialog "parkRunnerLily">>Mia, this is <<= $player.firstName>>. She just moved in.<</dialog>>
<<narrative>>Mia turned her phone in her hand as she looked at you. Her gaze was brief but direct. She tilted her head slightly.<</narrative>>
<<dialog "neighborMia">>Welcome.<</dialog>>
<<dialog "player">>Thanks.<</dialog>>
<<dialog "parkRunnerLily">>I'm trying to get her used to the city.<</dialog>>
<<narrative>>Mia's smile widened a little. She raised one eyebrow.<</narrative>>
<<dialog "neighborMia">>I can help with that sometimes... if you want.<</dialog>>
<<dialog "player">>Sure. Why not.<</dialog>>
<<narrative>>Just then, Mia's phone rang. She answered immediately, and her voice shifted at once, softer and almost like a purr.<</narrative>>
<<dialog "neighborMia">>Yeah, Ryan... I'm right outside... I'm talking to friends, two minutes... I'm coming.<</dialog>>
<<narrative>>She hung up. Mia rolled her eyes, then laughed. Lily laughed right after her.<</narrative>>
<<dialog "parkRunnerLily">>Is this one new?<</dialog>>
<<dialog "neighborMia">>Like you ever kept up with the last one.<</dialog>>
<<dialog "parkRunnerLily">>I can't even remember his name anymore.<</dialog>>
<<narrative>>Mia adjusted her bag on her shoulder.<</narrative>>
<<dialog "neighborMia">>Anyway, I should go. Nice meeting you, <<= $player.firstName>>.<</dialog>>
<<dialog "player">>Nice meeting you too.<</dialog>>
<<narrative>>After a few steps, Mia looked back over her shoulder. Her hair swept with the wind as she lifted one hand in a small wave.<</narrative>>
<<dialog "neighborMia">>I'm around. If you see me, don't be shy. We'll talk.<</dialog>>
<<narrative>>Then she took her phone back out and walked off with quick steps.<</narrative>>
<<narrative>>You and Lily started walking again. After a few steps, Lily spoke in a lower voice.<</narrative>>
<<dialog "parkRunnerLily">>Mia's got high energy. With guys, though... let's just say her life moves fast.<</dialog>>
<<dialog "player">>Yeah, I noticed.<</dialog>>
<<narrative>>Lily laughed, and you both kept walking with small smiles.<</narrative>>
<</nobr>><<nobr>>
<<silently>>
<<set $flags = $flags || {}>>
<<set $flags._actionTurnGuards = $flags._actionTurnGuards || {}>>
<<set _guardKey = "parkRunnerLilyJogTogether">>
<<set _alreadyProcessedThisTurn = ($flags._actionTurnGuards[_guardKey] || 0) === State.turns>>
<<set _ret = "sunsetPark">>
<<set $location = _ret>>
<<if !_alreadyProcessedThisTurn>>
<<set $flags._actionTurnGuards[_guardKey] = State.turns>>
<<requireMinEnergy 25 _ret "Not enough energy.">>
<<logDailyActivity "parkRunnerLily" "jogTogether">>
<<advanceTime 45 "relax">>
<<loseStat "energy" 15>>
<<loseStat "stress" 20>>
<<gainStat "mood" 4>>
<<gainStat "cardio" 1 25>>
<<gainStat "lowerBody" 1 15>>
<<gainSkill "physical" "running" 1 35>>
<<gainCharacterStat "parkRunnerLily" "friendship" 2>>
<<flushNotifications>>
<<recalculateStats>>
<<if $gameSettings.trackCalories>>
<<set $dailyCalorieIntake -= 300>>
<</if>>
<<set _friendshipNow = ($characters.parkRunnerLily && $characters.parkRunnerLily.stats && $characters.parkRunnerLily.stats.friendship) || 0>>
<<set _friendshipLevelNow = ($characters.parkRunnerLily && $characters.parkRunnerLily.stats && $characters.parkRunnerLily.stats.friendshipLevel) || 1>>
<<set $flags.lilyJogTogetherDone = true>>
<<set _showGymQuestEvent = (!$flags.lilyGymQuestOffered && _friendshipLevelNow >= 1 && _friendshipNow >= 30 && $cardio >= 15 && $lowerBody > 10)>>
<<set _miaUnknown = !($characters.neighborMia && $characters.neighborMia.known)>>
<<set _showMiaJogMeetEvent = (_miaUnknown && State.random() < (1 / 3))>>
<<set _jogTogetherVid = "assets/content/scenes/maplewood/park/jog/jogtogether" + random(1, 2) + ".mp4">>
<</if>>
<</silently>>
<<if !_alreadyProcessedThisTurn>>
<<if _showGymQuestEvent>>
<<include "parkRunnerLilyJogGymQuestEvent">>
<<else>>
<<if _showMiaJogMeetEvent>>
<<include "parkRunnerLilyJogMeetMiaEvent">>
<<else>>
<<set _gossipPool = setup.lilyJogGossip>>
<<set _pick = _gossipPool[Math.floor(State.random() * _gossipPool.length)]>>
<<print _pick.text>>
<</if>>
<<vid _jogTogetherVid "100%">>
<<narrative>>By the time you cool down, your legs are tired but your head feels lighter.<</narrative>>
<div class="location-actions">
<<talkBackButton _ret>>
</div>
<</if>>
<<else>>
<div class="location-actions">
<<talkBackButton _ret>>
</div>
<</if>>
<</nobr>><div class="divnobr">
<<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<set $location = $location || "sunsetPark">>
<<set $flags = $flags || {}>>
<<set _stats = $characters.parkRunnerLily.stats>>
<<set _level = _stats.friendshipLevel || 1>>
<<set _friendship = _stats.friendship || 0>>
<<set _showInviteEvent = (!$flags.lilyJogInviteSeen && _level >= 1 && _friendship >= 20)>>
<<if !_showInviteEvent>>
<<logDailyActivity "parkRunnerLily" "talk">>
<<updateCharacterLocations>>
<<set _phase = "common">>
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.lilyTopics && setup.lilyTopics[_phase] && setup.lilyTopics[_phase][_levelKey] && setup.lilyTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.lilyTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.lilyTopics && setup.lilyTopics.common && setup.lilyTopics.common[_levelKey] && setup.lilyTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.lilyTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(State.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(State.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "Lily gives you a quick smile and taps her watchshe's got to finish her cooldown and head out.", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
<<if _topic.friendship>><<gainCharacterStat "parkRunnerLily" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "parkRunnerLily" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "parkRunnerLily" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "parkRunnerLily" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</if>>
<</silently>>
<<if _showInviteEvent>>
<<include "parkRunnerLilyJogInviteEvent">>
<<else>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.parkRunnerLily.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>>
<</if>>
</div><<silently>>
<<set _parkActRet = $activityOrigin || "sunsetPark">>
<<requireMinEnergy 13 _parkActRet "Not enough energy.">>
<<set $location = $activityOrigin || "sunsetPark">>
<<advanceTime 15 "relax">>
<<addHeelsTravelMinutes 15>>
<<loseStat "energy" 3>>
<<loseStat "stress" 12>>
<<gainStat "mood" 5>>
<<flushNotifications>>
<<recalculateStats>>
<<run $(document).trigger(':passagerender')>>
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 and _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 and _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _sceneMode = random(1, 2)>>
<<if _sceneMode is 1>>
<<set _baseRoll = random(1, 4)>>
<<if _baseRoll is 1>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/walkingPark1.mp4">>
<<set _walkText = "You take a leisurely walk along the paths. Not a workout-just moving, taking in the trees and the light. Your steps settle into a steady rhythm and your head clears a little.">>
<<elseif _baseRoll is 2>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/walkingPark2.mp4">>
<<set _walkText = "You keep a comfortable pace and let your mind wander while the rustle of leaves and distant chatter blend into a calming rhythm.">>
<<elseif _baseRoll is 3>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/walkingPark3.mp4">>
<<set _walkText = "You follow the winding trail without rushing, taking in the greenery and giving yourself a few quiet minutes away from everything else.">>
<<else>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/birdScene1.mp4">>
<<set _walkText = "Two birds on a branch above you. One of them hops sideways for no reason. You stand there watching for longer than you meant to.">>
<</if>>
<<else>>
<<if _timeSlot is "morning">>
<<set _sceneRoll = random(1, 2)>>
<<if _sceneRoll is 1>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/morningScene1.mp4">>
<<set _walkText = "A sporty couple moves through their routine near the trail, focused and full of energy. The cool morning air and steady pace make the whole park feel fresh and alive.">>
<<else>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/morningScene2.mp4">>
<<set _walkText = "A couple rests after training, catching their breath on the side of the path. You keep walking at an easy pace, letting the quiet morning calm settle in.">>
<</if>>
<<elseif _timeSlot is "afternoon">>
<<set _sceneRoll = random(1, 4)>>
<<if _sceneRoll is 1>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/afternoonScene1-1.mp4">>
<<set _walkText = "Two women sit on the grass sharing a picnic and easy conversation. Around them, the park carries that bright midday buzz of people making the most of the day.">>
<<elseif _sceneRoll is 2>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/afternoonScene1-2.mp4">>
<<set _walkText = "The same picnic now feels more celebratory, with laughter and raised glasses in the sun. You pass by with a smile, carried along by the warm social energy.">>
<<elseif _sceneRoll is 3>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/afternoonScene2.mp4">>
<<set _walkText = "Four students walk past in animated conversation-two women, two men, all talking over each other in bursts of laughter. The path feels lively and full of motion.">>
<<else>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/afternoonScene3.mp4">>
<<set _walkText = "A couple has claimed a quiet patch of grass for their own little picnic. In the middle of the crowd, they still look like they found a private world.">>
<</if>>
<<else>>
<<set _sceneRoll = random(1, 3)>>
<<if _sceneRoll is 1>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/eveningScene1-1.mp4">>
<<set _walkText = "A couple walking ahead of you pauses for a kiss, unhurried and completely in their own world. The late-day light softens everything around the path.">>
<<elseif _sceneRoll is 2>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/eveningScene1-2.mp4">>
<<set _walkText = "The same couple has stopped talking. They're just sitting now. You walk past them without looking twice.">>
<<else>>
<<set _walkVid = "assets/content/scenes/maplewood/park/walk/eveningScene2.mp4">>
<<set _walkText = "On the grass, a couple huddles over a phone, sharing something that makes them both smile. Around them, the park feels calm and comfortably winding down.">>
<</if>>
<</if>>
<</if>>
<<set _bushEncounter = false>>
<<if _timeSlot is "evening">>
<<if ($contentPreferences.publicExhibition !== false) && random(1, 3) is 1>>
<<set _bushEncounter = true>>
<</if>>
<</if>>
<</silently>>
<<narrative "Park Walk">>
<<print _walkText>>
<</narrative>>
<<vid _walkVid "100%">>
<<if _bushEncounter>>
<<include "parkWalk_bushEncounter">>
<<else>>
<div class="location-actions">
<<btn "Finish" $location>><</btn>>
</div>
<</if>>
<<nobr>>
<<if $contentPreferences.publicExhibition === false>>
<<narrative "Park Walk">>
You catch a hint of movement and muffled voices in the bushes off the path. Whatever it is, you decide not to look, and keep walking.
<</narrative>>
<div class="location-actions">
<<btn "Finish" "sunsetPark">><</btn>>
</div>
<<else>>
<<silently>>
<<set $location = "sunsetPark">>
<<set $activityOrigin = $activityOrigin || "sunsetPark">>
<<if ndef $flags>><<set $flags = {}>><</if>>
<<set $flags.bushSet = random(1, 5)>>
<<set _basePath = "assets/content/scenes/maplewood/park/walk/encounter/" + $flags.bushSet + "/">>
<<gainStat "arousal" 2>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<if $flags.bushSet is 1>>
<<narrative "Park Walk">>
You're walking the trail when you hear it, a low, wet sound just off the path, coming from behind the thick brush to your left. You slow down. The bushes shift slightly. You take a few steps closer and part the branches.
<</narrative>>
<<set _bushVid = _basePath + "blowjob1.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
She's in no hurry. My heart slams once - and I still don't move.
<</dialog>>
<<elseif $flags.bushSet is 2>>
<<narrative "Park Walk">>
Something catches your eye, a movement in the bushes a few meters off the path, too deliberate to be the wind. You step off the trail, move quietly through the grass, and lean forward just enough to see past the leaves.
<</narrative>>
<<set _bushVid = _basePath + "bushSex1.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
Completely unbothered. My face goes hot before I can look away.
<</dialog>>
<<elseif $flags.bushSet is 3>>
<<narrative "Park Walk">>
A sound pulls you off the path, soft at first, easy to dismiss. Then you hear it again, closer. A breath caught short. A low voice. You push through the brush carefully and see a fallen log a few meters in, and the two people using it.
<</narrative>>
<<set _bushVid = _basePath + "bushSex1.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
Slow, deliberate - and the heat drops straight through me.
<</dialog>>
<<elseif $flags.bushSet is 4>>
<<narrative "Park Walk">>
The sound reaches you before the image does, rhythmic, slick, unmistakable. You follow it without fully deciding to, stepping off the gravel and between two thick trunks. Through the last layer of leaves you can see them clearly.
<</narrative>>
<<set _bushVid = _basePath + "bushSex1.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
Tongue, lips, hands - she's not rushing. My mouth's paper-dry.
<</dialog>>
<<else>>
<<narrative "Park Walk">>
You almost walk past them. Two people against a tree, close together, you assume they're kissing and look away. Then you catch the small sounds. The way her hips are moving. You take one step back and look again, properly this time.
<</narrative>>
<<set _bushVid = _basePath + "bushSex1.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
She knows exactly what she's doing. My breath won't steady.
<</dialog>>
<</if>>
<<narrative>>
Your heart is going faster than it should be. You know exactly what you're looking at. You should go. You're still standing there.
<</narrative>>
<<set _bushWatchNeedCorr = 1>>
<<set _corr = $corruption || 0>>
<<set _bushKeepLockedHint = "Need " + _bushWatchNeedCorr + " corruption">>
<div class="location-actions">
<<if _corr >= _bushWatchNeedCorr>>
<<btn "Keep watching" "parkWalk_bushWatch">><</btn>>
<<else>>
<<btn "Keep watching" "" "locked" _bushKeepLockedHint>><</btn>>
<</if>>
<<btn "Walk away" "sunsetPark">><</btn>>
</div>
<</if>>
<</nobr>>
<<nobr>>
<<silently>>
<<set $location = "sunsetPark">>
<<set $activityOrigin = $activityOrigin || "sunsetPark">>
<<set _bushParkReturn = "sunsetPark">>
<<set _bushWatchNeedCorr = 1>>
<<if $contentPreferences.publicExhibition === false>>
<<goto _bushParkReturn>>
<</if>>
<<if ($corruption || 0) < _bushWatchNeedCorr>>
<<run window.notifyWarning("You're not corrupted enough to keep watching.")>>
<<goto _bushParkReturn>>
<</if>>
<<set _basePath = "assets/content/scenes/maplewood/park/walk/encounter/" + $flags.bushSet + "/">>
<<gainStat "arousal" 5>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<if $flags.bushSet is 1>>
<<narrative "Park Walk">>She hasn't slowed down. If anything she's taking her time more deliberately now, working him with her mouth like she has nowhere else to be.<</narrative>>
<<set _bushVid = _basePath + "blowjob2.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
She's taking him so deep and she's not even gagging.
<</dialog>>
<<narrative>>He grips her hair. His breathing shifts. She doesn't stop, she goes deeper.<</narrative>>
<<set _bushVid = _basePath + "blowjob3.mp4">><<vid _bushVid "100%">>
<<narrative>>You back away from the brush on unsteady legs, pulse still loud in your ears when you reach the open path.<</narrative>>
<<elseif $flags.bushSet is 2>>
<<narrative "Park Walk">>She's still on her knees and she's into it, moaning around him, head moving in a slow rhythm that has nothing reluctant about it.<</narrative>>
<<set _bushVid = _basePath + "bushSex2.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
She's moaning around his cock like she's the one getting off on it.
<</dialog>>
<<narrative>>She pulls back, looks up at him, then climbs on top of him right there on the ground and sinks down onto his cock.<</narrative>>
<<set _bushVid = _basePath + "bushSex3.mp4">><<vid _bushVid "100%">>
<<narrative>>He rolls her onto her back and takes over, thrusting into her hard while she arches up into every movement.<</narrative>>
<<set _bushVid = _basePath + "bushSex4.mp4">><<vid _bushVid "100%">>
<<narrative>>He finishes in her mouth. She doesn't pull back.<</narrative>>
<<set _bushVid = _basePath + "bushSex5.mp4">><<vid _bushVid "100%">>
<<narrative>>You find the path again without really deciding to. The park sounds completely normal around you and somehow that makes it worse.<</narrative>>
<<elseif $flags.bushSet is 3>>
<<narrative "Park Walk">>His face is between her thighs and she's got both hands gripping the log, hips rolling into his mouth like she's chasing something.<</narrative>>
<<set _bushVid = _basePath + "bushSex2.mp4">><<vid _bushVid "100%">>
<<narrative>>She pulls him up by the hair and guides his cock back to her mouth. Not done. Wants more.<</narrative>>
<<set _bushVid = _basePath + "bushSex3.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
She just pulled him back to her mouth after he went down on her. She wants more.
<</dialog>>
<<narrative>>She turns around on top of him, reaches back to position him, and sinks down in a slow grind.<</narrative>>
<<set _bushVid = _basePath + "bushSex4.mp4">><<vid _bushVid "100%">>
<<narrative>>He grips her hips and flips her onto her hands and knees. She braces and takes it.<</narrative>>
<<set _bushVid = _basePath + "bushSex5.mp4">><<vid _bushVid "100%">>
<<narrative>>He pulls out and finishes on her. She stays still and lets him.<</narrative>>
<<set _bushVid = _basePath + "bushSex6.mp4">><<vid _bushVid "100%">>
<<narrative>>You turn and walk. The evening air hits your face and does almost nothing about the heat underneath it.<</narrative>>
<<elseif $flags.bushSet is 4>>
<<narrative "Park Walk">>She's working her way down slowly, tongue, lips, hands, completely unhurried, like she's savoring every inch of it.<</narrative>>
<<set _bushVid = _basePath + "bushSex2.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
She's moving on him so slowly, like she's feeling every inch. I'm wet and I haven't moved in minutes.
<</dialog>>
<<narrative>>He pulls her up by the arms and lifts her against the tree trunk, thrusting up into her hard.<</narrative>>
<<set _bushVid = _basePath + "bushSex3.mp4">><<vid _bushVid "100%">>
<<narrative>>He sets her down. She drops straight back to her knees and finishes him with her mouth.<</narrative>>
<<set _bushVid = _basePath + "bushSex4.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
The look she gave him after. Jesus.
<</dialog>>
<<narrative>>You slip back onto the path. Your legs feel strange and the heat low in your belly doesn't go anywhere no matter how fast you walk.<</narrative>>
<<else>>
<<narrative "Park Walk">>She drops to her knees in front of him right there in the dirt, pulls his jeans open, and takes him in her mouth like she's been waiting all evening for exactly this.<</narrative>>
<<set _bushVid = _basePath + "bushSex2.mp4">><<vid _bushVid "100%">>
<<narrative>>She pulls back, wipes her mouth, and climbs on top of him without a word.<</narrative>>
<<set _bushVid = _basePath + "bushSex3.mp4">><<vid _bushVid "100%">>
<<dialog "player">>
(Inner voice)
The way she moaned when he slid into her, I felt that between my own legs.
<</dialog>>
<<narrative>>He grabs her hips and flips her onto her hands and knees. She pushes back into every thrust and asks for more.<</narrative>>
<<set _bushVid = _basePath + "bushSex4.mp4">><<vid _bushVid "100%">>
<<narrative>>He pulls her head back. She opens her mouth and tilts her chin up.<</narrative>>
<<set _bushVid = _basePath + "bushSex5.mp4">><<vid _bushVid "100%">>
<<narrative>>You walk away on legs that don't feel entirely yours. Your face is flushed, your panties are ruined, and the park carries on like nothing happened at all.<</narrative>>
<</if>>
<div class="location-actions">
<<btn "Leave quietly" "sunsetPark">><</btn>>
</div>
<</nobr>>
<<silently>>
<<set _parkActRet = $activityOrigin || "sunsetPark">>
<<requireMinEnergy 25 _parkActRet "Not enough energy.">>
/* Set location from activity origin */
<<set $location = $activityOrigin>>
/* Run the yoga activity */
<<set $daily.yogaDone = true>>
<<advanceTime 45 "relax">>
<<loseStat "energy" 15>>
<<loseStat "stress" 10>>
<<gainSkill "physical" "yoga" 3 20>>
<<recalculateStats>>
/* Text variations */
<<set _variation = random(1, 3)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "You find a quiet spot on the grass and roll out your yoga mat. The outdoor setting makes the practice feel more grounding. Birds chirp overhead as you move through your poses. The fresh air and nature around you enhance every stretch.">>
<<case 2>>
<<set _sceneText = "The park is peaceful this time of day. You set up your mat under a large tree and begin your routine. A few curious onlookers pass by, but you stay focused. By the end, you feel centered and refreshed.">>
<<case 3>>
<<set _sceneText = "Practicing yoga outdoors hits different. The uneven ground challenges your balance in new ways. The breeze on your skin, the sounds of nature - it all adds to the experience. You hold each pose longer, really feeling the connection between your body and the earth.">>
<</switch>>
/* Random yoga video: yogaPark1.mp4 – yogaPark8.mp4 */
<<set _vidNum = random(1, 8)>>
<<set _yogaVid = "assets/content/scenes/maplewood/park/yoga/yogaPark" + _vidNum + ".mp4">>
<</silently>>
<<narrative "Park Yoga">>
<<print _sceneText>>
<</narrative>>
<<vid _yogaVid "100%">>
<div class="location-actions">
<<btn "Finish" $location>><</btn>>
</div><<nobr>><<set $location = "parlorMassage">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Lotus Spa">>A massage parlor with extra services. Discretion guaranteed.<</narrative>>
<<showLocationChars "parlorMassage">>
<<navMenu>><<navCard "redLightCenter">><</navMenu>><<nobr>><<set $location = "pethouseSuite">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Pinnacle">>A luxurious penthouse with breathtaking city views. The pinnacle of urban living.<</narrative>>
<<showLocationChars "pethouseSuite">>
<<navMenu>><<navCard "towerC">><</navMenu>><<nobr>>
<<set $location = "pharmacy">>
<<set _shopItems = ["toothpaste", "wet_wipes"]>>
<<set _shopBack = "oldTown">>
<<updateCharacterLocations>>
<<narrative "Green Cross Pharmacy">>A neighborhood pharmacy run by a friendly pharmacist. Medications and basic health supplies.<</narrative>>
<<showLocationChars "pharmacy">>
<<shop "Green Cross Pharmacy" "Health & Beauty" _shopItems _shopBack>>
<</nobr>><<nobr>><<set $location = "pier">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Pier">>A wooden pier extending over the water. Fishermen cast lines while couples walk hand in hand.<</narrative>>
<<showLocationChars "pier">>
<<navMenu>><<navCard "pierIcecream">><<navCard "marinaBay">><</navMenu>><<nobr>><<set $location = "pierIcecream">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Scoops & Swirls">>A cheerful ice cream stand at the end of the pier. Cold treats on a hot day.<</narrative>>
<<showLocationChars "pierIcecream">>
<<navMenu>><<navCard "pier">><</navMenu>><<nobr>><<set $location = "policeStation">><</nobr>>
<<updateCharacterLocations>>
<<narrative "5th Precinct">>The local police precinct. Officers patrol the area and keep the peace.<</narrative>>
<<showLocationChars "policeStation">>
<<navMenu>><<navCard "civicCenter">><</navMenu>><<nobr>><<set $location = "postOffice">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Old Town Post Office">>A charming old post office. Sending letters and packages to loved ones.<</narrative>>
<<showLocationChars "postoffice">>
<<navMenu>><<navCard "civicCenter">><</navMenu>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<div class="prologue-content-area">
<div class="prologue-header">
<div class="prologue-age-badge">Present Day</div>
<h1 class="prologue-title">Your Room</h1>
<div class="prologue-header-line"></div>
</div>
<<image "assets/content/locations/maplewood/familyHouse/fhBedroom.webp" "100%">>
<<narrative>>
The door closes softly behind you.
<</narrative>>
<<narrative>>
You stand in the doorway. It's smaller than the old one. Or maybe it just feels smaller because you haven't decided where anything goes yet.
<</narrative>>
<<narrative>>
The room is yours... but it doesn't feel like it yet.
<</narrative>>
<<narrative>>
The bed is against the far wall, perfectly made. Your desk sits by the window, empty except for a lamp dad must have placed there. The closet door is open, waiting.
<</narrative>>
<<narrative>>
Everything's clean. New. Impersonal.
<</narrative>>
<<narrative>>
You walk to the window.
<</narrative>>
<<image "assets/content/scenes/prologue/bedroomView.webp" "100%">>
<<narrative>>
The street stretches below. Houses line both sides, trees dotting the sidewalks. Evening is approaching. Lights are starting to flicker on in neighbors' windows.
<</narrative>>
<<narrative>>
It's quiet. Too quiet.
<</narrative>>
<<narrative>>
Back in the old town, your window faced the main street. You knew the rhythm. Mrs. Chen walking her dog at 6pm. The ice cream truck on Thursdays. Kids playing ball until someone's mom called them in.
<</narrative>>
<<narrative>>
This street? Strangers. New rhythms you haven't learned yet.
<</narrative>>
<<narrative>>
You turn away from the window.
<</narrative>>
<<image "assets/content/scenes/prologue/backpack.webp" "100%">>
<<narrative>>
Your small backpack sits on the bed where you dropped it. Right. Unpack.
<</narrative>>
<<narrative>>
You unzip it, pull out clothes. T-shirts, jeans, underwear. The essentials you kept with you for the drive. You carry them to the closet, start hanging things up.
<</narrative>>
<<narrative>>
When you're done, you step back and look.
<</narrative>>
<<narrative>>
More than half the closet is empty.
<</narrative>>
<<narrative>>
Your eyes scan the room. Nothing under the bed. Nothing behind the desk. The closet is still empty.
<</narrative>>
<<narrative>>
Your suitcase.
<</narrative>>
<<narrative>>
The big black one with most of your clothes. Hoodies, jackets, your good jeans. Where is it?
<</narrative>>
<<narrative>>
You check the doorway. Maybe dad left it there.
<</narrative>>
<<narrative>>
Nothing.
<</narrative>>
<<narrative>>
Heart sinking, you realize it's not here.
<</narrative>>
<<dialog "player">>
(Inner voice)
Did it stay in the car? Did the movers lose it?
<</dialog>>
<<narrative>>
Most of your wardrobe is in that suitcase.
<</narrative>>
<<dialog "player">>
(Inner voice)
I need to ask.
<</dialog>>
<<narrative>>
You walk to the door and step out into the hallway.
<</narrative>>
<<narrative>>
Time to go downstairs.
<</narrative>>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-primary">Go downstairs</span>' "prologueDownstairsAsk">><</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<div class="prologue-content-area">
<div class="prologue-header">
<div class="prologue-age-badge">Present Day</div>
<h1 class="prologue-title">Dinner</h1>
<div class="prologue-header-line"></div>
</div>
<<narrative>>
Dad's voice carries from the living room.
<</narrative>>
<<dialog "father">>
By the way, I'm going to the office tomorrow afternoon.
<</dialog>>
<<dialog "mother">>
(looking over) Interview?
<</dialog>>
<<dialog "father">>
Yeah, at 1. We'll discuss when I start, I'll take a look at the workplace.
<</dialog>>
<<narrative>>
Dad's new job. The reason you're all here.
<</narrative>>
<<dialog "mother">>
Good. You must be excited.
<</dialog>>
<<dialog "father">>
I'll take $characters.brother.firstName with me. After the office, we'll stop by the high school and handle his registration.
<</dialog>>
<<narrative>>
Your brother groans from somewhere upstairs. He must've heard.
<</narrative>>
<<dialog "father">>
We'll leave at 1. Should be back by 3 or 4.
<</dialog>>
<<dialog "mother">>
(turning to you) Okay. I'll do some more organizing around here then. Will you help me?
<</dialog>>
<<dialog "player">>
Sure.
<</dialog>>
<<narrative>>
Mom's expression shiftssomething more serious now.
<</narrative>>
<<dialog "mother">>
By the way, $player.firstName... Have you looked at universities here? What are you thinking?
<</dialog>>
<<narrative>>
The question hangs in the air.
<</narrative>>
<<narrative>>
You're quiet for a moment. You haven't decided. Haven't really thought about it, if you're honest. Too much happening.
<</narrative>>
<<dialog "player">>
I don't know... I haven't decided yet.
<</dialog>>
<<dialog "mother">>
Are you thinking about a gap year, or starting right away?
<</dialog>>
<<dialog "player">>
I still... don't know.
<</dialog>>
<<narrative>>
Mom reaches out, touches your arm.
<</narrative>>
<<dialog "mother">>
Don't rush. Think about it, decide when you're ready. We'll talk about it.
<</dialog>>
<<narrative>>
You nod, grateful she's not pushing.
<</narrative>>
<<narrative>>
The doorbell rings.
<</narrative>>
<<narrative>>
Dad stands.
<</narrative>>
<<dialog "father">>
It's here.
<</dialog>>
<<narrative>>
He walks to the door. A few seconds later, he returns with two large pizza boxes, the smell filling the room immediately.
<</narrative>>
<<dialog "father">>
Dinner's ready!
<</dialog>>
<<narrative>>
Your brother appears at the top of the stairs, taking them two at a time.
<</narrative>>
<<dialog "brother">>
Finally. I'm starving.
<</dialog>>
<<narrative>>
Everyone gathers at the table.
<</narrative>>
<<narrative>>
Dad opens the boxes. Five-cheese for you, meat lovers for your brother. Steam rises, cheese bubbles, the scent of oregano and tomato sauce thick in the air.
<</narrative>>
<<image "assets/content/scenes/prologue/kitchenNightPizza.webp" "100%">>
<<narrative>>
Mom sits down, looking at all of you.
<</narrative>>
<<dialog "mother">>
Our first meal together here.
<</dialog>>
<<dialog "father">>
(raising his water glass) To new beginnings.
<</dialog>>
<<dialog "mother">>
To new beginnings.
<</dialog>>
<<narrative>>
Your brother grabs a slice immediately. You follow suit.
<</narrative>>
<<narrative>>
Your first dinner in the new house.
<</narrative>>
<<narrative>>
It feels... almost normal.
<</narrative>>
<<narrative>>
Almost.
<</narrative>>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-primary">Continue</span>' "prologueNightEnd">><</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<div class="prologue-content-area">
<div class="prologue-header">
<div class="prologue-age-badge">Present Day</div>
<h1 class="prologue-title">Downstairs</h1>
<div class="prologue-header-line"></div>
</div>
<<image "assets/content/locations/maplewood/familyHouse/fhUpperstairs.webp" "100%">>
<<narrative>>
You step out of your room.
<</narrative>>
<<narrative>>
The hallway is quiet. Music still drifts from your brother's room, muffled and bass-heavy.
<</narrative>>
<<narrative>>
You walk to the stairs.
<</narrative>>
<<narrative>>
Each step creaks slightly under your feet. As you descend, the sounds of home life rise to meet you. Cabinet doors. Running water. Your parents' voices.
<</narrative>>
<<image "assets/content/scenes/prologue/fhLivingroomEvening.webp" "100%">>
<<narrative>>
You reach the bottom.
<</narrative>>
<<narrative>>
Mom and dad are in the living room, both on the couch. Coffee cups in hand, talking quietly. They look relaxed for the first time today.
<</narrative>>
<<narrative>>
Mom notices you first.
<</narrative>>
<<dialog "mother">>
So, how's the room, sweetie? Do you like it?
<</dialog>>
<<narrative>>
You hesitate, then decide to just ask.
<</narrative>>
<<dialog "player">>
Mom, dad... Where's my big suitcase?
<</dialog>>
<<narrative>>
Mom's expression shifts. Confusion, then realization.
<</narrative>>
<<dialog "mother">>
Your suitcase? Isn't it in the car?
<</dialog>>
<<dialog "player">>
No, I checked.
<</dialog>>
<<dialog "father">>
(setting down his coffee cup) That one was with the moving company.
<</dialog>>
<<narrative>>
Your stomach drops.
<</narrative>>
<<dialog "player">>
What do you mean with the moving company?
<</dialog>>
<<dialog "father">>
We had too much stuff. Couldn't fit everything in the car and the first truck. The last batch is coming tomorrow.
<</dialog>>
<<dialog "player">>
Tomorrow?
<</dialog>>
<<dialog "father">>
Yeah, tomorrow afternoon. Probably around 4 or 5.
<</dialog>>
<<narrative>>
The disappointment must show on your face.
<</narrative>>
<<dialog "mother">>
(reaching out) Don't worry, honey. You have enough clothes for today, right?
<</dialog>>
<<dialog "player">>
Yeah... I guess.
<</dialog>>
<<dialog "mother">>
It'll be here tomorrow. Just be patient.
<</dialog>>
<<dialog "father">>
(leaning forward) Maybe tomorrow you can go out, explore the neighborhood a bit. Get familiar with the area.
<</dialog>>
<<dialog "mother">>
That's a good idea. You can see your new surroundings.
<</dialog>>
<<narrative>>
You nod, not really feeling it.
<</narrative>>
<<narrative>>
The room feels suddenly smaller. Tomorrow. Your stuff arrives tomorrow. One more day of feeling... incomplete.
<</narrative>>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-primary">Continue</span>' "prologueEvening">><</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>>
<<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<div class="prologue-content-area">
<div class="prologue-header">
<div class="prologue-age-badge">Present Day</div>
<h1 class="prologue-title">Evening</h1>
<div class="prologue-header-line"></div>
</div>
<<narrative>>
Mom stands up from the couch, stretching.
<</narrative>>
<<dialog "mother">>
(heading toward the kitchen) Let's order pizza. First night, no need to cook.
<</dialog>>
<<dialog "father">>
(looking up) Good idea. What should we get?
<</dialog>>
<<dialog "mother">>
(calling back) Two large. One five-cheese, $player.firstName likes that.
<</dialog>>
<<narrative>>
A small smile touches your face. She remembered.
<</narrative>>
<<dialog "father">>
And the other?
<</dialog>>
<<dialog "mother">>
Meat lovers. Pepperoni, sausage, whatever they have. For $characters.brother.firstName.
<</dialog>>
<<dialog "father">>
(pulling out his phone) Alright, calling now.
<</dialog>>
<<narrative>>
Mom comes back from the kitchen, looking at you.
<</narrative>>
<<dialog "mother">>
$player.firstName, go get your brother. Tell him we ordered pizza.
<</dialog>>
<<narrative>>
You nod and head back toward the stairs.
<</narrative>>
<<narrative>>
The climb up feels different now. Lighter, maybe. Pizza. A family tradition. First night in a new place, always pizza.
<</narrative>>
<<image "assets/content/scenes/prologue/stairsNight.webp" "100%">>
<<narrative>>
In the hallway, your brother's door is still half-open.
<</narrative>>
<<narrative>>
You knock lightly.
<</narrative>>
<<dialog "player">>
Hey.
<</dialog>>
<<narrative>>
You peek inside.
<</narrative>>
<<image "assets/content/scenes/prologue/brotherRoomNight.webp" "100%">>
<<narrative>>
His room mirrors yours but it's already his. Blue-grey walls, sports posters up, computer set up by the window. A few boxes sit unpacked by the bed.
<</narrative>>
<<narrative>>
He's at his desk, headphones on, focused on his screen. He notices you and pulls them off.
<</narrative>>
<<dialog "brother">>
What's up?
<</dialog>>
<<dialog "player">>
Come downstairs. We ordered pizza.
<</dialog>>
<<dialog "brother">>
(glancing back at his screen) When's it coming?
<</dialog>>
<<dialog "player">>
30-40 minutes. Come get ready.
<</dialog>>
<<dialog "brother">>
Okay, let me finish this.
<</dialog>>
<<narrative>>
You look around his room.
<</narrative>>
<<dialog "player">>
At least you've settled in.
<</dialog>>
<<dialog "brother">>
(shrugging) Enough. I'll do the rest later.
<</dialog>>
<<dialog "player">>
(smiling) Sure.
<</dialog>>
<<narrative>>
You head back downstairs.
<</narrative>>
<<image "assets/content/scenes/prologue/kitchenNight.webp" "100%">>
<<narrative>>
Mom is in the kitchen now, moving around the cabinets, pulling out plates.
<</narrative>>
<<dialog "mother">>
Did you tell him?
<</dialog>>
<<dialog "player">>
Yeah, he's coming.
<</dialog>>
<<dialog "mother">>
Good. (handing you the plates) Let's set the table. We can eat as soon as it arrives.
<</dialog>>
<<narrative>>
You carry the plates to the dining table, start arranging them. Forks, napkins, glasses.
<</narrative>>
<<narrative>>
Everything in its place.
<</narrative>>
<<narrative>>
Mom watches from the kitchen doorway.
<</narrative>>
<<dialog "mother">>
(softly) Our first dinner here.
<</dialog>>
<<narrative>>
There's something in her voice. Hope, maybe, with a little sadness underneath.
<</narrative>>
<<dialog "player">>
Yeah...
<</dialog>>
<<dialog "mother">>
We'll get used to it. (more to herself) Every new beginning is like this.
<</dialog>>
<<narrative>>
You finish setting the table. Everything's ready.
<</narrative>>
<<narrative>>
Now you wait.
<</narrative>>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-primary">Continue</span>' "prologueDinner">><</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<div class="prologue-content-area">
<div class="prologue-header">
<div class="prologue-age-badge">Present Day</div>
<h1 class="prologue-title">First Night</h1>
<div class="prologue-header-line"></div>
</div>
<<narrative>>
The pizza boxes are empty. Everyone's full, tired.
<</narrative>>
<<dialog "father">>
(leaning back, patting his stomach) That was good. I can't eat another bite.
<</dialog>>
<<dialog "mother">>
(smiling) First night pizza tradition.
<</dialog>>
<<dialog "brother">>
(standing, stretching) I'm exhausted. I'm heading up.
<</dialog>>
<<narrative>>
He walks toward the stairs without waiting for a response.
<</narrative>>
<<dialog "mother">>
Good night, honey.
<</dialog>>
<<dialog "brother">>
Night.
<</dialog>>
<<narrative>>
Dad stands too, gathering empty plates.
<</narrative>>
<<dialog "father">>
I'm going up as well. Early day tomorrow.
<</dialog>>
<<dialog "mother">>
Okay. I'll clean up a bit.
<</dialog>>
<<narrative>>
Dad pauses by your chair, rests a hand on your shoulder for a moment.
<</narrative>>
<<dialog "father">>
Good night, $player.firstName. Your first night in the new house.
<</dialog>>
<<narrative>>
There's weight in his voice. Pride, maybe. Or sadness. Or both.
<</narrative>>
<<dialog "player">>
Good night, dad.
<</dialog>>
<<narrative>>
He heads upstairs. You and mom are alone now.
<</narrative>>
<<narrative>>
She starts clearing the table, moving quietly. You help, carrying glasses to the kitchen.
<</narrative>>
<<dialog "mother">>
(softly) You go rest too. It's been a long day.
<</dialog>>
<<dialog "player">>
(nodding) Good night, mom.
<</dialog>>
<<narrative>>
She turns, pulls you into a hug. Brief but tight.
<</narrative>>
<<dialog "mother">>
(whispering) It's going to be okay. I promise.
<</dialog>>
<<narrative>>
You're not sure if she's trying to convince you or herself.
<</narrative>>
<<narrative>>
You pull away, head for the stairs.
<</narrative>>
<<narrative>>
The house is quieter now. Your brother's music has stopped. Dad's probably already in bed. Only mom remains downstairs, finishing up.
<</narrative>>
<<image "assets/content/scenes/prologue/bedroomNight.webp" "100%">>
<<narrative>>
You climb the stairs slowly.
<</narrative>>
<<narrative>>
At the top you pause in the hallway. Your brother's door is closed, a band of blue glowing under it. Phone, not lamp.
<</narrative>>
<<narrative>>
Your door is slightly open, the way you left it.
<</narrative>>
<<narrative>>
You step inside and close it behind you.
<</narrative>>
<<narrative>>
The room is dark except for the streetlight filtering through the window. You don't turn on the lamp. Instead, you walk to the bed and sit on the edge.
<</narrative>>
<<dialog "player">>
(Inner voice)
First night. New house. New city.
<</dialog>>
<<narrative>>
You change into your pajamas, climb under the covers.
<</narrative>>
<<narrative>>
The bed feels different. Firmer than your old one. The pillows smell like detergent, not like home.
<</narrative>>
<<narrative>>
You stare at the ceiling.
<</narrative>>
<<narrative>>
Back home, you could hear the old house settling, the familiar creaks and groans. Here, everything's silent. Too silent.
<</narrative>>
<<narrative>>
Your mind wanders to the old street. To Sam's driveway, where you left your bike for three days last summer before anyone noticed. That's what gone means.
<</narrative>>
<<dialog "player">>
(Inner voice)
And ahead? Unknown.
<</dialog>>
<<dialog "player">>
(Inner voice)
Maybe I'll be okay.
<</dialog>>
<<dialog "player">>
(Inner voice)
Maybe.
<</dialog>>
<<narrative>>
Your eyes grow heavy.
<</narrative>>
<<narrative>>
You let them close.
<</narrative>>
<<narrative>>
Tomorrow is another day. Tomorrow, you'll wake up in this new room, in this new house, in this new city.
<</narrative>>
<<narrative>>
Tomorrow, something new begins.
<</narrative>>
<<narrative>>
But for now, you sleep.
<</narrative>>
<div style="text-align: center; margin-top: 60px; font-size: 1.2em; color: #666; font-style: italic;">
First night ends...
</div>
<div class="prologue-btn-row" style="margin-top: 40px;">
<<link '<span class="prologue-btn prologue-btn-primary">Next Day</span>' "nextDayMorning">><</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>><<nobr>>
<<set $hideTopbarNav = true>>
<<set $hideTopbarTimebox = true>>
<<set $hideTopbarNotifications = true>>
<<set $hideRightbar = true>>
<<set $hideTopbar = true>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="prologue-page">
<div class="prologue-layout-wrapper">
<div class="prologue-content-area">
<div class="prologue-header">
<div class="prologue-age-badge">Present Day</div>
<h1 class="prologue-title">A New Beginning</h1>
<div class="prologue-header-line"></div>
</div>
<<image "assets/content/scenes/prologue/road.webp" "100%">>
<<narrative>>
The highway stretches endlessly ahead. Fields on both sides, wire fences, the occasional tractor... Miles of the same view.
<</narrative>>
<<narrative>>
You're in the back seat, staring out the window. Your brother's asleep next to you, headphones still in. Mom and dad haven't talked since the last gas station. The radio plays some 80s song dad knows all the words to but isn't singing.
<</narrative>>
<<narrative>>
That street keeps running through your mind. Your brother learning to ride a bike on the sidewalk. Mom reading to you in the backyard until the sun dropped. It's all behind you now.
<</narrative>>
<<narrative>>
Then a sign appears: "City Center - 30km"
<</narrative>>
<<narrative>>
Your stomach drops. Thirty kilometers. That's it? Your new life is that close... You don't know if you're excited or scared. Maybe both.
<</narrative>>
<<dialog "mother">>
So, $player.firstName, what are you thinking for this week? You gonna start working on those college applications?
<</dialog>>
<<dialog "player">>
I don't know... Let's just get there first.
<</dialog>>
<<dialog "mother">>
Fair enough. You packed everything though, right? It's pretty far, you can't just run back if you forgot something.
<</dialog>>
<<dialog "father">>
Twenty minutes out, by the way.
<</dialog>>
<<narrative>>
You look out the window. No more fieldsbuildings are starting to appear. Small businesses. Roadside shops. Traffic lights.
<</narrative>>
<<narrative>>
The city's getting closer.
<</narrative>>
<<narrative>>
You close your eyes. The memories come...
<</narrative>>
<div class="prologue-flashback">
<div class="prologue-flashback-line"></div>
<span class="prologue-flashback-text">Memories</span>
<div class="prologue-flashback-line"></div>
</div>
<div class="prologue-btn-row">
<<link '<span class="prologue-btn prologue-btn-primary">Return to the Past</span>' "earlyYears">><</link>>
</div>
</div>
</div>
</div>
</div>
<</nobr>>
/* ========================================
CHECK OLD TOWN - Single scene
Location: Old Town. Narrative + pharmacy & barber images, discover, complete.
======================================== */
<div class="divnobr">
<<nobr>>
<<silently>>
<<set $location = "oldTown">>
<<advanceTime 10>>
<<discover "Pharmacy" "Green Cross Pharmacy">>
<<discover "ShopBarber" "The Clipper Shop">>
<<completeQuest "check_old_town">>
<</silently>>
<<narrative "Old Town">>
You take a look around Old Town. Among the cobblestone streets and vintage storefronts you spot a pharmacy and a barber shopa proper women's salon. Good to know where they are for later.
<</narrative>>
<<image "assets/content/locations/oldTown/pharmacy.webp" "100%">>
<<narrative>>
The Green Cross Pharmacy. A neighborhood spot for medications and basics.
<</narrative>>
<<image "assets/content/locations/oldTown/shopBarber.webp" "100%">>
<<narrative>>
The Clipper Shop. You make a mental note of both places.
<</narrative>>
<div class="btn-center">
<<btn "Back" "oldTown">><</btn>>
</div>
<</nobr>>
</div>/* ========================================
FIND JOB - Accept flow: Return to Ruby's, clerk at door
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<advanceQuestStage "find_job">>
<</silently>>
<<nobr>>
<<narrative "Ruby's Diner">>
You walk over to Ruby's Diner and step through the door.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_entrance_01.webp" "100%">>
<<narrative>>
Red vinyl booths, the smell of coffee and grease, a few customers. Behind the countera familiar face. The guy who greeted you last time. He looks up, sees you, and gives a slight smile.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_entrance_02.webp" "100%">>
<<dialog "dinerClerk">>
Oh, you're back. Something you need?
<</dialog>>
<<dialog "player">>
I need to see Vince. About the job… we talked before. I've thought about it, and I've made a decision.
<</dialog>>
<<dialog "dinerClerk">>
Vince? Sure, go ahead. Head to the backif he's free, he'll talk. Last door at the end of the corridor, just knock.
<</dialog>>
<<narrative>>
He waves you toward the back. You pass the kitchen; a narrow corridor, dressing room and storage on the left, a closed door at the end. Vince's office.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Go to Vince's office" "quest_find_job_accept_manager">><</btn>>
</div>
</div>/* ========================================
FIND JOB - Accept flow: Manager room, say you want to start, accepted, start tomorrow
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<advanceTime 15>>
<</silently>>
<<nobr>>
<<narrative "Manager's office">>
You knock and step inside. Vince is behind the deskhe remembers you, raises his eyebrows slightly. He gestures for you to take a seat.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_manager_room_01.webp" "100%">>
<<dialog "dinerManager">>
So? Made up your mind, I take it.
<</dialog>>
<<dialog "player">>
Yeah. I thought about it… I'd like to accept the dishwashing position.
<</dialog>>
<<dialog "dinerManager">>
Alright. Start tomorrow. First day we'll get you a uniform, show you where everything goes.
<</dialog>>
<<dialog "player">>
What time should I be here?
<</dialog>>
<<dialog "dinerManager">>
We open at ten. Be here then, that's all.
<</dialog>>
<<dialog "player">>
Okay. I'll be here tomorrow at ten.
<</dialog>>
<<dialog "dinerManager">>
Good. See you tomorrow.
<</dialog>>
<<narrative>>
You stand up. You leave the office, walk down the corridor to the front of the diner. You start tomorrowfor now, you're free.
<</narrative>>
<</nobr>>
<<silently>>
<<jobResetState>>
<<set $job.id = "ruby_dishwasher">>
<<set $job.tier = 1>>
<<set _accJob = setup.jobs && setup.jobs[$job.id]>>
<<if _accJob>><<set $characters.player.occupation = _accJob.position + ", " + _accJob.workplaceName>><</if>>
<<phoneUnlockContact "dinerManager">>
<<if !$phoneConversations["dinerManager"] || $phoneConversations["dinerManager"].length === 0>>
<<phoneReceiveMessage "dinerManager" "This is Vince. Save this number. If you can't make a shift, message me before start time.">>
<</if>>
<<run (function(){
var t = State.variables.timeSys;
var d = new Date(t.year, t.month - 1, t.day);
d.setDate(d.getDate() + 1);
State.variables.job.startedOn = { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };
})()>>
<<completeQuest "find_job">>
<</silently>>
<<run window.notifySuccess("You start as dishwasher at Ruby's Diner tomorrow.")>>
<div class="btn-center">
<<btn "Continue" "dinerRubys">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - CAREER SERVICES KIOSK
Player speaks with the career services clerk
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "townHall">>
<<advanceQuestStage "find_job">>
<</silently>>
<<nobr>>
<<narrative "Kiosk #3">>
You walk up to the kiosk and sit down in the chair facing the clerk.
She's a woman in her forties, glasses perched on her nose, expression neutral and businesslike. Her name tag reads "Sandra Wilson - Career Counselor."
She doesn't look up from her computer screen right away.
<</narrative>>
<<image "assets/content/scenes/oldtown/cityHall/questJob/townHall_career_services_kiosk.webp" "100%">>
<<dialog "careerClerk">>
Name?
<</dialog>>
<<narrative>>
Straight to business. No pleasantries.
<</narrative>>
<<dialog "player">>
<<= $player.firstName>> <<= $player.lastName>>.
<</dialog>>
<<narrative>>
She types something, still not looking at you.
<</narrative>>
<<dialog "careerClerk">>
Age?
<</dialog>>
<<dialog "player">>
<<= $timeSys.year - $characters.player.birthYear>>.
<</dialog>>
<<narrative>>
More typing. Her fingers move quickly across the keyboard, mechanical and efficient.
<</narrative>>
<<dialog "careerClerk">>
Previous work experience?
<</dialog>>
<<dialog "player">>
Uh... none, actually. I just moved to the city.
<</dialog>>
<<narrative>>
For the first time, she pauses. Her fingers stop typing.
<</narrative>>
<<dialog "careerClerk">>
No work experience at all?
<</dialog>>
<<dialog "player">>
No. I'm new here. Just arrived a few days ago.
<</dialog>>
<<narrative>>
She types a few more things, her expression unchanged. You can't tell if this is good or bad.
<</narrative>>
<<dialog "careerClerk">>
Education level?
<</dialog>>
<<dialog "player">>
High school graduate.
<</dialog>>
<<narrative>>
More typing. The clicking of keys fills the silence.
Then she stops, staring at her screen, scanning through something. Her eyes move back and forth, reading.
The wait feels longer than it probably is.
<</narrative>>
<<narrative>>
Finally, she looks up at you. Really looks at you, for the first time since you sat down.
Her expression is... not encouraging.
<</narrative>>
<<dialog "careerClerk">>
Alright. Based on your profile - no experience, just arrived in the city - your options are limited.
<</dialog>>
<<narrative>>
Your stomach sinks a little. You expected this, but hearing it out loud still stings.
<</narrative>>
<<dialog "careerClerk">>
I have one current opening that matches your criteria.
<</dialog>>
<<narrative>>
She clicks a few more times, then turns the monitor slightly so you can see.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Look at the screen" "quest_find_job_ruby_diner_offer">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - CAREER SERVICES QUEUE
Player waits in a long line at Career Services
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "townHall">>
<<advanceQuestStage "find_job">>
<</silently>>
<<nobr>>
<<narrative "Career Services Office">>
You push open the door and step into the Career Services office.
The receptionist wasn't kidding about it being busy.
<</narrative>>
<<image "assets/content/scenes/oldtown/cityHall/questJob/townHall_career_services_queue.webp" "100%">>
<<narrative>>
The room is packed. A long line of people snakes from the service kiosks all the way to the entrance. You count quickly - at least fifteen people ahead of you.
Some look young, around your age. Others are older, middle-aged. All of them have the same expression: tired resignation mixed with hope.
<</narrative>>
<<narrative "The Wait">>
You take your place at the end of the line.
The wait begins.
<</narrative>>
<<narrative>>
Minutes tick by. The line moves slowly - painfully slowly. Each person at the kiosks seems to take forever, discussing their employment history, skills, available positions...
<</narrative>>
<<narrative>>
You shift your weight from foot to foot. Check your phone. Look around the room. There's not much to see - generic motivational posters on the walls ("Your Future Starts Here!"), a water cooler in the corner, uncomfortable plastic chairs filled with waiting people.
<</narrative>>
<<narrative>>
More time passes. The line inches forward. You've moved up maybe three spots in what feels like forever.
You understand now why the receptionist gave you that sympathetic look.
<</narrative>>
<<narrative>>
You zone out a bit, lost in thought. Thinking about the apartment, about money, about needing this job search to work out...
<</narrative>>
<<narrative "Finally...">>
When you snap back to attention, you realize you've moved up significantly. Only two people ahead of you now.
Almost there.
<</narrative>>
<<narrative>>
The person in front of you finishes up and walks away, looking frustrated. You don't know if that's a good sign or not.
One more person... and then it's your turn.
<</narrative>>
<<narrative "Your Turn">>
The clerk at kiosk number three waves you over.
Here goes nothing.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Approach the kiosk" "quest_find_job_career_services_kiosk">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - CIVIC CENTER EXPLORATION
Player explores Old Town and discovers key locations
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "oldTown">>
<<updateCharacterLocations>>
<<discover "CivicCenter" "Civic Center">>
<<discover "TownHall" "Town Hall">>
<<discover "HighSchool" "Old Town High School">>
<<discover "Hospital" "Hospital">>
<<discover "PoliceStation" "Police Station">>
<<advanceQuestStage "find_job">>
<</silently>>
<<nobr>>
<<narrative "Old Town - Main Street">>
You arrive at Old Town. The area is bustling with activity - older buildings with character, tree-lined streets, people going about their day.
Marcus mentioned the Civic Center is around here, somewhere near Town Hall. You start walking, taking in your surroundings.
<</narrative>>
<<image "assets/content/locations/oldTown/oldTown.webp" "100%">>
<<narrative>>
As you walk down the main street, you start noticing the different buildings. This area feels more established, more... official than your neighborhood.
<</narrative>>
<<narrative "Old Town - Civic Center Area">>
You turn a corner and the atmosphere shifts. The buildings here are larger, more institutional. You've found the civic center district.
<</narrative>>
<<image "assets/content/locations/oldTown/civicCenter.webp" "100%">>
<<narrative>>
You slow down, looking around. This is clearly the heart of Old Town's administrative area. You start exploring to get your bearings.
<</narrative>>
<<narrative "High School">>
The first building you notice is a large structure with students milling around outside. A sign identifies it as "Old Town High School."
Your brother mentioned his school is in this area. This must be it.
<</narrative>>
<<image "assets/content/locations/oldTown/civicCenter/highSchool.webp" "100%">>
<<narrative "Hospital">>
Further down the street, you see a modern building with a red cross symbol. The hospital. Clean, professional, with ambulances parked near the emergency entrance.
Good to know where this is, just in case.
<</narrative>>
<<image "assets/content/locations/oldTown/civicCenter/hospital.webp" "100%">>
<<narrative "Police Station">>
Next to the hospital, a sturdy building with police cruisers parked outside. The police station. The American flag waves from a pole out front.
You make note of the location - always useful to know where the police station is in a new city.
<</narrative>>
<<image "assets/content/locations/oldTown/civicCenter/policeStation.webp" "100%">>
<<narrative "Town Hall">>
And then you see it - at the end of the street, an impressive building with grand columns and wide steps leading up to the entrance. A sign reads "Town Hall."
This is it. This is where the Career Services office is located, where you can find job listings.
You found it.
<</narrative>>
<<image "assets/content/locations/oldTown/civicCenter/townHall.webp" "100%">>
<<narrative>>
You take a moment to absorb everything. You've mapped out the civic district - the high school, hospital, police station, and most importantly, Town Hall with its Career Services office.
You know where to go now for the job search.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Step inside" "townHall">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - CORNER SHOP, MARCUS (Redirecting to Civic Center)
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "storeCorner">>
<<updateCharacterLocations>>
<<advanceTime 8>>
<<gainCharacterStat "shopClerkMarcus" "friendship" 5>>
<<discover "OldTown" "Old Town">>
<<advanceQuestStage "find_job">>
<</silently>>
<<narrative "Quick Mart">>
You step inside. Close to home, a familiar place. Worth a try.
<</narrative>>
<<narrative>>
There are a few people at the register, Marcus is helping customers. You get in line.
<</narrative>>
<<narrative>>
When it's your turn, Marcus looks up and his face lights up when he sees you.
<</narrative>>
<<dialog "shopClerkMarcus">>
(Smiling)
Oh, $player.firstName! Welcome. What are you getting today, another map?
(In a playful tone)
Or are you starting a brochure collection now?
<</dialog>>
<<dialog "player">>
(Laughing)
No, no. Actually, I didn't come to buy anything, I came about the sign in the window.
<</dialog>>
<<dialog "shopClerkMarcus">>
(Raising an eyebrow)
The cashier posting?
(Smiling and pointing at the register)
Ooo, look at that. I got that position, just a few days ago. So you're after my job, huh?
<</dialog>>
<<dialog "player">>
(Laughs a bit embarrassed)
I just saw it in the window and got curious.
<</dialog>>
<<dialog "shopClerkMarcus">>
(Waving his hand)
Just kidding. We forgot to take down the sign, good you reminded me.
(In a more serious tone)
Are you really looking for a job?
<</dialog>>
<<dialog "player">>
Yeah. I figured this would be close to home, thought I'd give it a shot.
<</dialog>>
<<dialog "shopClerkMarcus">>
(Nodding understandingly)
Makes sense. But unfortunately this spot's filled.
(With a thoughtful expression)
Not many open positions around here right now. But have you checked out Career Services at the Civic Center? You know, the office inside Town Hall.
<</dialog>>
<<dialog "player">>
Not yet, actually. I saw the name when I was looking online but... you're right, I should probably go there directly.
<</dialog>>
<<dialog "shopClerkMarcus">>
(Enthusiastically)
Yeah yeah, that place exists exactly for people like you. Job postings, application help and all that... My friend applied there last month, it's a pretty solid place.
(Smiling)
And it's free too, you just need to go in and talk to them.
<</dialog>>
<<dialog "player">>
Okay, I'll do that then. Thanks Marcus, I appreciate the help.
<</dialog>>
<<dialog "shopClerkMarcus">>
(Warmly)
No problem $player.firstName. I hope you find a good job. We'll see you around anyway, I'm curious how it goes.
(Adds playfully)
Maybe one day you'll be working nearby somewhere, we'll meet up on coffee breaks.
<</dialog>>
<<dialog "player">>
(Laughing)
Who knows! See you Marcus.
<</dialog>>
<<dialog "shopClerkMarcus">>
See you..
<</dialog>>
<<narrative>>
You leave the shop. Marcus is right about where he reminded you - Civic Center, Career Services. Going there makes sense.
<</narrative>>
<div class="btn-center">
<<btn "Leave the shop" "maplewood">><</btn>>
</div>
</div>/* ========================================
FIND JOB - Family Dinner 18:00 (Scene 1: Table)
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhKitchen">>
<<eatFood 750 100>>
<<loseStat "thirst" 100>>
<<gainStat "bladder" 15>>
<<gainStat "energy" 45>>
<<loseStat "stress" 10>>
<<gainStat "mood" 5>>
<<gainStat "health" 5>>
<<advanceTime 30 "relax">>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<<narrative "Family Dinner">>
You sit down at the table last. Plates are full; your father has his fork in hand, your mother is pouring water. Your face is a little down. Your father notices right away.
<</narrative>>
<<image "assets/content/scenes/maplewood/familyHouse/kitchen/questJob/family_dinner_table.webp" "100%">>
<<dialog "father">>
What's wrong, sweetie? Something on your mind? You've barely said a word today.
<</dialog>>
<<narrative>>
A beat of silence. You don't want to answer, but the question hangs there and you feel awkward.
<</narrative>>
<<dialog "player">>
…I was at Town Hall all day looking for work. I waited in line, filled out forms. But there weren't many listings. And with no experience, there were hardly any options.
<</dialog>>
<<dialog "mother">>
(Placing a hand on your shoulder)
Don't let it get to you. You're just getting started.
<</dialog>>
<<dialog "player">>
I'm not, really. I mean… I did find something. There's a place in Old Town called Ruby's Diner. I went for the waitress job but…
(Your voice drops a little)
I guess that position was filled. They offered me dishwashing. I said I wanted to think about it. Didn't give them an answer.
<</dialog>>
<<dialog "father">>
(Taking a bite, thinking)
Ruby's Diner… Actually it's not that far. But you know best, sweetie. Whatever you're comfortable with.
<</dialog>>
<<dialog "mother">>
You have to start somewhere. If you're thinking about it, just go for it. You'll earn some money, get some experience. You can always move on later if you want.
<</dialog>>
<<narrative>>
You're quiet for a moment. What they said sinks in.
<</narrative>>
<<narrative>>
They're right. You have to start somewhere. Money's tight, the move's still fresh, every dollar counts. Dishwashing is still a start. Nothing to be ashamed of.
<</narrative>>
<<narrative>>
You focus on your plate again; you feel a bit lighter.
<</narrative>>
<<dialog "father">>
(Gesturing at the plates)
You like it? Your mother did good today.
<</dialog>>
<<dialog "mother">>
(Smiling)
You say that every day and it's enough.
<</dialog>>
<<narrative>>
A short silenceclink of cutlery, maybe your father saying something else or the TV in the background. The meal goes on.
<</narrative>>
<<dialog "father">>
I'm off early again tomorrow. What about you, sweetieTown Hall again?
<</dialog>>
<<dialog "player">>
I don't know. Maybe I'll stop by Ruby's again and give them an answer.
<</dialog>>
<<narrative>>
Dinner continues like thattable talk, maybe your brother chimes in or your father glances at the paper. The meal ends with dessert or tea; everyone gets up.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Help with the dishes" "quest_find_job_family_dinner_dishes">><</btn>>
</div>
</div>/* ========================================
FIND JOB - Family Dinner (Scene 2: Dishes, confession, $40)
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhKitchen">>
<<advanceTime 45 "relax">>
<<advanceQuestStage "find_job">>
<<gainCharacterStat "mother" "friendship" 2>>
<</silently>>
<<nobr>>
<<narrative "Kitchen – After Dinner">>
Dinner is over. Your mother gathers the plates; you follow her into the kitchen. There's a dishwasher: you rinse at the sinkscraping bits off, quick rinse under the tapthen pass each piece to her and she loads the machine. Hot water, soap, the clink of plates. She turns on the radio or hums something. You stay quiet. One plate, then another. Then you can't hold it in anymore.
<</narrative>>
<<image "assets/content/scenes/maplewood/familyHouse/kitchen/questJob/family_dinner_dishes_sink.webp" "100%">>
<<dialog "player">>
Mom.
<</dialog>>
<<dialog "mother">>
Hmm?
<</dialog>>
<<dialog "player">>
Mom… what I said at the table wasn't really true.
<</dialog>>
<<narrative>>
You pause. The water's still running. You can't look at her.
<</narrative>>
<<dialog "player">>
At Ruby's, the ownerVincehe said, "Do you ever look in the mirror?" That I looked… unkempt. That for the front they need someone put together.
<</dialog>>
<<dialog "mother">>
He said that? To your face?
<</dialog>>
<<dialog "player">>
(Your voice drops)
Then he offered dishwashing. "In the back. No one has to see you there." I told you guys I'd think about it. I didn't tell you the real reason.
<</dialog>>
<<narrative>>
A short silence. Water runs; the plate goes from your hands to hers.
<</narrative>>
<<dialog "mother">>
(She slots it into the dishwasher, then turns. Her voice is soft but clear.)
Sweetie, that's nonsense. You're beautiful. Don't listen to that.
<</dialog>>
<<dialog "player">>
I'm not saying I'm making it up. He really said it. It still hurt.
<</dialog>>
<<dialog "mother">>
(She steps closer, rests a hand on your dry shoulder.)
Listen. We just moved, everything's been turned upside down. When you're stressed, everything feels bigger. They'd find another excuse even if you were perfectly put together. What matters is what you believe about yourself. If it helps, get yourself a few things tomorrow. You'll feel better.
<</dialog>>
<<narrative>>
You keep rinsing. She reassures you again; if you still blame yourself, she insists it wasn't your fault.
<</narrative>>
<<dialog "mother">>
(Drying her hands, then pulling out some cash from her pocket or a drawerforty dollars.)
Here. If it'll make you feel better, buy some lotion or something. Whatever you need. I don't want you stuck in your head like this.
<</dialog>>
<<narrative>>
She holds out the money. You take it. You keep rinsing for a while; then she leaves you to it.
<</narrative>>
<</nobr>>
<<silently>>
<<earnMoney 40 "Mom gave you $40">>
<<set $mom_dinner_talk = true>>
<<advanceQuestStage "find_job">>
<<set $mom_suggested_mall = true>>
<<startQuest "check_old_town">>
<</silently>>
<div class="btn-center">
<<btn "Continue" "fhKitchen">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - CORRIDOR / MANAGER DOOR
Player walks down the corridor and knocks
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<set $characters.dinerManager.known = true>>
<</silently>>
<<nobr>>
<<narrative "Back corridor">>
You walk down the corridor. On your left you pass two doorsone labeled "Dressing room," the other "Storage." At the end is a closed door with no sign. Manager's office.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_corridor_01.webp" "100%">>
<<narrative>>
You knock.
<</narrative>>
<<dialog "dinerManager">>
Come in.
<</dialog>>
<<narrative>>
You open the door and step inside.
<</narrative>>
<<dialog "player">>
Hi. I'm here to apply for the job. The waitress position.
<</dialog>>
<</nobr>>
<div class="btn-center">
<<btn "Enter the office" "quest_find_job_ruby_diner_manager_room">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - RUBY'S DINER ENTRANCE
Player enters with the address slip, goes to register
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<set $characters.dinerClerk.known = true>>
<<if !$characters.dinerClerk.firstMet>><<set $characters.dinerClerk.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>><</if>>
<<gainCharacterStat "dinerClerk" "friendship" 2>>
<</silently>>
<<nobr>>
<<narrative "Ruby's Diner">>
You step inside and take a quick look around. Red vinyl booths, the smell of coffee and grease, a few customers. You clutch the address slip from Career Services and head for the register.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_entrance_01.webp" "100%">>
<<narrative>>
A man in his thirties is at the counterfriendly face, like he's been working here for years. He looks up as you approach.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_entrance_02.webp" "100%">>
<<dialog "dinerClerk">>
Hey. What can I get you?
<</dialog>>
<<dialog "player">>
Hi. I'm here about the job. The waitress position? I got this from Career Services.
<</dialog>>
<<narrative>>
You hold out the slip. He takes it, glances at it, and nods.
<</narrative>>
<<dialog "dinerClerk">>
Oh, you're here for that. Good. Boss handles all hiringI'll take you back. Come on.
<</dialog>>
<<narrative>>
He comes out from behind the counter and leads you toward the back. You pass the kitchen, then a door that opens onto a narrow corridor.
<</narrative>>
<<dialog "dinerClerk">>
Dressing room and storage are on the left. Manager's office is at the end. Just knock.
<</dialog>>
<<narrative>>
He points down the hall, gives you an encouraging look, and heads back to the front.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Walk to the manager's office" "quest_find_job_ruby_diner_corridor">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - MANAGER'S OFFICE
Interview: rejection as waitress, dishwashing offer
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<advanceTime 30>>
<<advanceQuestStage "find_job">>
<<set $characters.dinerManager.known = true>>
<<if !$characters.dinerManager.firstMet>><<set $characters.dinerManager.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>><</if>>
<<gainCharacterStat "dinerManager" "friendship" 2>>
<</silently>>
<<nobr>>
<<narrative "Manager's office">>
A man in his forties looks up from behind the desk. His eyes linger a second longer than they should. Then he smiles.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_manager_room_01.webp" "100%">>
<<dialog "dinerManager">>
Welcome. Have a seat. Right there in front of the desk.
<</dialog>>
<<narrative>>
You sit down. He leans back and folds his hands.
<</narrative>>
<<dialog "dinerManager">>
So you're interested in working here. I'm Vince. I own this place. And you are?
<</dialog>>
<<dialog "player">>
<<= $player.firstName>> <<= $player.lastName>>. I just moved to the city. I don't have experience, but I'm a hard worker.
<</dialog>>
<<dialog "dinerManager">>
<<= $player.firstName>>. Nice to meet you.
<</dialog>>
<<narrative>>
He keeps looking at you in a way that makes you want to fold your arms. You wait for him to continue.
<</narrative>>
<<dialog "dinerManager">>
Look, I'll be straight with you. I can't take you on as waitress.
<</dialog>>
<<dialog "player">>
Oh. Can I ask why?
<</dialog>>
<<dialog "dinerManager">>
Do you ever look at yourself in the mirror?
<</dialog>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_manager_room_02.webp" "100%">>
<<narrative>>
You freeze. The words hit like a slap.
<</narrative>>
<<dialog "dinerManager">>
I meanyou're a bit unkempt. For the front, we need someone who looks put together. Customers notice. So that position isn't going to work.
<</dialog>>
<<narrative>>
You feel your face burn. You keep your voice steady. You don't let it show.
<</narrative>>
<<dialog "player">>
I see.
<</dialog>>
<<narrative>>
You're about to stand up and leave when he holds up a hand.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_manager_room_03.webp" "100%">>
<<dialog "dinerManager">>
Wait. If you want, I could set you up with a dishwashing job. Back of house. No one has to see you there. Same building, steady hours. What do you say?
<</dialog>>
<<dialog "player">>
I... I'd need to know more. Pay, hours. And I'd like some time to think about it.
<</dialog>>
<<dialog "dinerManager">>
Sure. Take your time. When you've decided, just come back and ask for me.
<</dialog>>
<<image "assets/content/scenes/oldtown/rubysDiner/questJob/diner_manager_room_04.webp" "100%">>
<<narrative>>
You get up. Your chest is tight, but you keep it together. You walk out of the office, down the corridor, and back into the diner. You don't look back.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Leave Ruby's Diner" "dinerRubys">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - RUBY'S DINER OFFER
Clerk offers waitress position at Ruby's Diner
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "townHall">>
<<advanceTime 66>>
<<discover "DinerRubys" "Ruby's Diner">>
<<discover "DinerRubysBathroom" "Bathroom">>
<<advanceQuestStage "find_job">>
<</silently>>
<<nobr>>
<<narrative "Job Listing">>
You lean forward to read the screen.
<</narrative>>
<<image "assets/content/scenes/oldtown/cityHall/questJob/townHall_ruby_offer_screen.webp" "100%">>
<<narrative>>
Computer Screen
<</narrative>>
<</nobr>>
<span style="font-family: monospace; padding: 10px; display: block; border: 1px solid #ccc; white-space: pre-line; margin: 1rem 0;">
JOB ID: #2847
POSITION: Waitress / Server
EMPLOYER: Ruby's Diner
LOCATION: Oldtown District, Main Street
HOURS: Part-time / Full-time Available
EXPERIENCE REQUIRED: None
</span>
<<nobr>>
<<dialog "careerClerk">>
Ruby's Diner. It's a local restaurant in the Oldtown district. They're looking for waitstaff. No experience required.
<</dialog>>
<<narrative>>
She prints out a small slip of paper and hands it to you.
<</narrative>>
<<dialog "careerClerk">>
Here's the address and contact information. You'll need to go there in person to apply. Ask for Ruby - she's the owner.
<</dialog>>
<<dialog "player">>
Thank you.
<</dialog>>
<<dialog "careerClerk">>
It's entry-level work, but it's steady employment. Good tips if you're pleasant with customers.
<</dialog>>
<<narrative>>
She's already typing again, ready for the next person.
<</narrative>>
<<dialog "careerClerk">>
Good luck.
<</dialog>>
<<narrative>>
You take the paper and stand up, feeling a mix of relief and nervousness.
It's not much, but it's a start. A real job opportunity.
<</narrative>>
<<narrative>>
You walk out of the Career Services office, clutching the address slip.
Ruby's Diner. Oldtown district.
You know where you need to go next.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Leave Town Hall" "townHall">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - TOWN HALL ENTRANCE
Player enters Town Hall and looks for Career Services
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "townHall">>
<<updateCharacterLocations>>
<<advanceQuestStage "find_job">>
<</silently>>
<<nobr>>
<<narrative "Town Hall - Main Lobby">>
You push through the heavy wooden doors and step into Town Hall's main lobby. The interior is impressive - high ceilings, marble floors, and the echo of footsteps bouncing off stone walls.
<</narrative>>
<<image "assets/content/scenes/oldtown/cityHall/questJob/townHall_interior.webp" "100%">>
<<narrative>>
The lobby is busy with people coming and going. Some are dressed in business attire, others in casual clothes. Everyone seems to know where they're going.
You don't.
<</narrative>>
<<narrative>>
You look around, trying to get your bearings. There are hallways branching off in different directions, signs pointing to various offices and departments. It's a bit overwhelming.
<</narrative>>
<<narrative "Information Desk">>
Near the center of the lobby, you spot a large information desk with a sign above it: "INFORMATION & VISITOR SERVICES."
A woman in her fifties sits behind the desk, typing on a computer. She looks like she's been working here for years.
That's probably a good place to start.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Approach the information desk" "quest_find_job_town_hall_information">><</btn>>
</div>
</div>/* ========================================
SCENE: FIND JOB - INFORMATION DESK
Player asks for directions to Career Services
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "townHall">>
<<advanceQuestStage "find_job">>
<</silently>>
<<nobr>>
<<narrative "Information Desk">>
You walk up to the information desk. The woman looks up from her computer, offering a professional but tired smile.
<</narrative>>
<<image "assets/content/scenes/oldtown/cityHall/questJob/townHall_information_desk.webp" "100%">>
<<dialog "townHallReceptionist">>
Good morning. How can I help you?
<</dialog>>
<<dialog "player">>
Hi, I'm looking for the Career Services office? I need to find job listings.
<</dialog>>
<<narrative>>
She nods knowingly, like she's answered this question a hundred times before.
<</narrative>>
<<dialog "townHallReceptionist">>
Career Services, of course. Take the hallway to your right, past the Public Records office. You'll see a sign for Employment & Career Services. Can't miss it.
<</dialog>>
<<dialog "player">>
Thank you.
<</dialog>>
<<dialog "townHallReceptionist">>
Fair warning - it can get busy in there. Especially in the mornings.
<</dialog>>
<<narrative>>
She gives you a sympathetic look before turning back to her computer.
You follow her directions, heading down the right hallway.
<</narrative>>
<<narrative>>
Finding Career Services
<</narrative>>
<<narrative>>
The hallway is lined with frosted glass doors, each labeled with different department names. You pass "Public Records," "City Planning," "Building Permits"...
<</narrative>>
<<narrative>>
And then you see it: "EMPLOYMENT & CAREER SERVICES" in bold letters on a door at the end of the hall.
You found it.
<</narrative>>
<</nobr>>
<div class="btn-center">
<<btn "Enter Career Services" "quest_find_job_career_services_queue">><</btn>>
</div>
</div>/* ==========================================
NEWCOMER QUEST - FIRST SHOPPING
Scene: Map discovery at Quick Mart
========================================== */
<div class="divnobr">
<<nobr>>
<<set $location = "storeCorner">>
<<updateCharacterLocations>>
<<discover "StoreCorner" "Quick Mart">>
<<completeQuest "first_shopping">>
<<set $showMapTutorial = true>>
<<set $showRelationTutorial = true>>
<<set $hideRightbarMap = false>>
<</nobr>>
<<narrative "Quick Mart - Counter">>
A small convenience store. You grab what your mom asked for and head to the counter. After paying, you notice a stack of brochures on the side of the counter. "City Map" is written on top.
<</narrative>>
<<dialog "player">>
(Points at the brochures)
What are these?
<</dialog>>
<<dialog "shopClerkMarcus">>
City map.
(Picks one up and hands it to you)
<</dialog>>
<<image "assets/content/scenes/maplewood/cornerShop/cityMap.webp" "100%">>
<<narrative>>
You take it and start looking it over without unfolding it.
<</narrative>>
<<dialog "player">>
Thanks, how much?
<</dialog>>
<<dialog "shopClerkMarcus">>
Free, take it. If you haven't seen this map until now, I'm guessing you just moved here?
(Smiles slightly)
<</dialog>>
<<dialog "player">>
Yeah, we just moved. The street with the big tree.
<</dialog>>
<<dialog "shopClerkMarcus">>
(Nods)
I know that place.
(Points through the window, opposite direction)
I live on the other side, over there.
<</dialog>>
<<dialog "player">>
(Looks where he's pointing)
<</dialog>>
<<dialog "shopClerkMarcus">>
I'm Marcus, I work here. I know pretty much everyone in the neighborhood. Welcome to the neighborhood.
<</dialog>>
<<dialog "player">>
(Smiles)
I'm $player.firstName.
<</dialog>>
<<dialog "shopClerkMarcus">>
(Smiles)
Nice to meet you.
<</dialog>>
<<dialog "player">>
Nice to meet you too.
<</dialog>>
<<narrative>>
You walk out with a smile, brochure in hand.
<</narrative>>
<<silently>>
<<advanceTime 5>>
/* Mark character as met and known */
<<set $characters.shopClerkMarcus.known = true>>
<<set $characters.shopClerkMarcus.firstMet = `${$timeSys.day}/${$timeSys.month}/${$timeSys.year}`>>
<<gainCharacterStat "shopClerkMarcus" "friendship" 5>>
<</silently>>
<div class="btn-center">
<<btn "Leave the Store" "maplewood">><</btn>>
</div>
</div><div class="divnobr">
<<nobr>>
<<silently>>
<<set $location = "downTown">>
<<advanceTime 6>>
<<if ndef $flags>><<set $flags = {}>><</if>>
<<set $flags.firstDowntownVisit = true>>
<<advanceQuestStage "go_to_mall">>
<<updateCharacterLocations>>
<</silently>>
<<narrative "Downtown">>
You step off the bus and the city hits you all at once.
<</narrative>>
<<vid "assets/content/scenes/downtown/cityLandscape.mp4" "100%">>
<<narrative>>
The noise. The crowd. The sheer //scale// of it – buildings that seem to scrape the sky, streets packed with people in every direction. Maplewood felt quiet, almost sleepy. This is something else entirely. Your chest tightens. You didn't expect it to feel this big.
You walk forward anyway. One step, then another. The crowd flows around you – suits, tourists, someone shouting into their phone. You keep your head down and follow the main strip. The air smells like exhaust and street food. Car horns. A distant siren.
<</narrative>>
<<vid "assets/content/scenes/downtown/cityCrowd.mp4" "100%">>
<<narrative>>
Then, at the end of the block, you see it.
A massive glass building rises above the storefronts. Three floors. Bright signs. People streaming in and out of the entrance. The crowd thickens as you get closer – families with shopping bags, couples, groups of teenagers. Everyone moving with purpose.
//Metro Shopping Center.// Lily was right. You can't miss it.
You pause at the doors. The buzz from inside spills out – music, voices, the hum of escalators. For a moment you think of the park. The quiet bench. The pond. You take a breath. You know where to go when you're ready.
<</narrative>>
<<image "assets/content/locations/downTown/mall.webp">>
<div class="location-actions">
<<btn "Continue" "downTown">><</btn>>
</div>
<</nobr>>
</div><div class="divnobr">
<<silently>>
<<set $location = "floorGround">>
<<advanceTime 8>>
<<if ndef $flags>><<set $flags = {}>><</if>>
<<set $flags.firstMallVisit = true>>
<<completeQuest "go_to_mall">>
<<updateCharacterLocations>>
/* Discover all mall locations */
<<set $discoveredMall = true>>
<<set $discoveredFloorGround = true>>
<<set $discoveredFloorSecond = true>>
<<set $discoveredFloorThird = true>>
<<set $discoveredStoreClothingA = true>>
<<set $discoveredStoreCosmetics = true>>
<<set $discoveredStoreElectronics = true>>
<<set $discoveredStoreShoeA = true>>
<<set $discoveredStoreSports = true>>
<<set $discoveredStoreBags = true>>
<<set $discoveredStoreClothingB = true>>
<<set $discoveredStoreClothingC = true>>
<<set $discoveredStoreJewelry = true>>
<<set $discoveredStoreLingerieA = true>>
<<set $discoveredStoreLingerieB = true>>
<<set $discoveredStoreShoeB = true>>
<<set $discoveredFoodCourtMall = true>>
<<set $discoveredSalonBeautyMall = true>>
<<set $discoveredTheatreMovie = true>>
<</silently>>
<<narrative "Metro Shopping Center">>
You're inside.
<</narrative>>
<<image "assets/content/locations/downTown/mall/floorGround.webp">>
<<narrative>>
The space opens up – high ceilings, skylights, gleaming floors. Escalators climb to upper levels. Storefronts line the corridor in both directions: clothing, shoes, electronics. Music plays overhead. The air is cool, conditioned. Nothing like the sticky heat outside.
<</narrative>>
<<narrative>>
You stand there for a moment, taking it in. The Quick Mart in Maplewood could fit in a corner of this place. The family house, the park, the quiet streets – it all feels very far away. Very small. You're not sure if that's comforting or overwhelming.
<</narrative>>
<<narrative>>
Shoppers pass by. A mother herds two kids toward a toy store. A couple laughs at something on a phone. Everyone seems to belong here. You take a slow breath. Maybe you can too.
<</narrative>>
<<narrative>>
You walk a few steps down the corridor. Store windows flash with mannequins and sale signs. Somewhere to your left you catch a glimpse of athletic wear – a sporty logo, racks of leggings and tops. //Lily's store.// You'll come back for that.
<</narrative>>
<<narrative>>
For now, you've seen enough. You know where everything is. The mall isn't going anywhere.
<</narrative>>
<div class="location-actions">
<<btn "Look Around" "floorGround">><</btn>>
</div>
</div><div class="divnobr">
<<nobr>>
<<silently>>
<<set $location = "gym">>
<<if !$characters.gymReceptionNora.known>>
<<set $characters.gymReceptionNora.known = true>>
<</if>>
<<if !$characters.gymReceptionNora.firstMet>>
<<set $characters.gymReceptionNora.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>>
<</if>>
<<set $flags = $flags || {}>>
<<gainCharacterStat "gymReceptionNora" "friendship" 1>>
<</silently>>
<<narrative "Iron Works Gym">>
As you pushed the door open, cool air hit your face. Air conditioning, and a faint disinfectant scent. The reception area was clean and organized, and the woman behind the desk looked up the moment she saw you.
<</narrative>>
<<dialog "gymReceptionNora">>Welcome. I'm Nora, how can I help you?<</dialog>>
<<dialog "player">>Hi, I'm <<= $player.firstName>>. A friend recommended this place, so I wanted to stop by and take a look.<</dialog>>
<<dialog "gymReceptionNora">>Nice to meet you, <<= $player.firstName>>. Tell your friend thanks from us. First time here?<</dialog>>
<<dialog "player">>Yeah.<</dialog>>
<<narrative>>
Nora leaned lightly on the counter, relaxed and friendly.
<</narrative>>
<<dialog "gymReceptionNora">>Want a quick tour?<</dialog>>
<div class="location-actions">
<<btn "Continue" "quest_lily_gym_frontdesk_intro_part2">><</btn>>
</div>
<</nobr>>
</div>
/* ========================================
SCENE 3: FATHER'S BAD NEWS
======================================== */
<div class="divnobr">
<<nobr>>
<<set $location = "fhKitchen">>
<<updateCharacterLocations>>
<<advanceQuestStage "moving_troubles">>
<</nobr>>
<<narrative "Kitchen">>
Your dad and brother walk in. Your brother looks normal but dad... tired, distant.
<</narrative>>
<<dialog "mother">>
Welcome back. How did the interview go?
<</dialog>>
<<dialog "father">>
(Smiles slightly but his eyes are tired)
It went well, I start Monday.
<</dialog>>
<<dialog "mother">>
(Happy)
Oh that's great! Such good news.
<</dialog>>
<<dialog "father">>
Yeah but...
(Pauses)
<</dialog>>
<<dialog "mother">>
But?
<</dialog>>
<<dialog "father">>
(Sighs, leans against the counter)
I called the moving company on the way back. To ask where the truck was.
<</dialog>>
<<dialog "mother">>
...and?
<</dialog>>
<<dialog "father">>
They had an accident.
<</dialog>>
<<dialog "mother">>
What?
<</dialog>>
<<dialog "father">>
On the highway. Truck tipped over, the container got damaged.
<</dialog>>
<<dialog "player">>
Did something happen to our stuff?
<</dialog>>
<<dialog "father">>
(Looks at you, sad)
Most of it's damaged sweetie. They say it can't be saved.
<</dialog>>
<<narrative>>
Silence for a moment.
<</narrative>>
<<dialog "brother">>
So... our stuff?
<</dialog>>
<<dialog "father">>
Yeah. Clothes, electronics, some of the furniture...
<</dialog>>
<<dialog "player">>
(Frowns)
Wait, what do you mean can't be saved? Is it all gone?
<</dialog>>
<<dialog "father">>
Not all of it but... most of it.
<</dialog>>
<<dialog "player">>
My laptop? My clothes?
<</dialog>>
<<dialog "father">>
(Nods)
Most likely, yeah.
<</dialog>>
<<dialog "player">>
(Disappointed)
Great. So I have nothing except what I'm wearing right now.
<</dialog>>
<<dialog "mother">>
(Sits down)
Insurance will cover it, right?
<</dialog>>
<<dialog "father">>
They will but it takes time. Weeks, maybe months.
<</dialog>>
<<dialog "player">>
Months? What am I supposed to do until then?
<</dialog>>
<<dialog "father">>
(Tired)
We'll manage with what we have for now. We already spent a lot on the move - deposit, first month's rent, bills... We need to be careful with money for a bit.
<</dialog>>
<<dialog "player">>
(Not angry but clearly upset)
So we can't buy anything new for now.
<</dialog>>
<<dialog "father">>
Not right away. But we'll figure something out.
<</dialog>>
<<dialog "mother">>
(Calm, trying to hold things together)
We'll find a way sweetie. Let's not panic for now.
<</dialog>>
<<dialog "player">>
(Stands up)
I'm... going to my room for a bit.
<</dialog>>
<<dialog "mother">>
(Understanding)
Okay. Rest a bit, we'll talk later.
<</dialog>>
<<dialog "father">>
(Softly, as you walk away)
We'll figure it out, I promise.
<</dialog>>
<<narrative>>
You head toward the stairs without answering. Your head's a mess. You're not angry but... the disappointment is heavy.
<</narrative>>
<div class="btn-center">
<<btn "Go to your room" "quest_moving_troubles_room_scene">><</btn>>
</div>
</div>/* ========================================
SCENE 2: TALK TO MOTHER
======================================== */
<div class="divnobr">
<<nobr>>
<<set $location = "fhKitchen">>
<<advanceQuestStage "moving_troubles">>
<</nobr>>
<<narrative "Kitchen - Family House">>
You find your mother in the kitchen, unpacking boxes and trying to organize things.
<</narrative>>
<<dialog "player">>
I'm back mom, got the groceries.
<</dialog>>
<<dialog "mother">>
(Looks up and smiles)
Oh you're back, thanks sweetie. How was it, did you find everything okay?
<</dialog>>
<<dialog "player">>
Yeah, no problem. The store is really close actually.
(Sets the bag on the counter)
Look, I even found a city map at the store. There's a park nearby too.
<</dialog>>
<<dialog "mother">>
(Looks at the map, smiles)
Oh nice, got yourself a new toy huh.
(Starts putting groceries away)
Ahh a park nearby is great, perfect for walks when you're bored.
(Pauses for a moment)
Though our neighborhood is really nice for that too, look at all the trees, wide streets...
<</dialog>>
<<dialog "player">>
(Smiles)
Yeah, I noticed on the way back. It's a quiet place.
<</dialog>>
<<dialog "mother">>
(Sighs, happy but tired)
We'll get used to it here... I just wish the moving truck would arrive so we can finally settle in.
<</dialog>>
<<dialog "player">>
What did dad say, the truck was coming today right?
<</dialog>>
<<dialog "mother">>
(Pauses)
Yeah, he called me earlier. He said they're on their way with $characters.brother.firstName.
<</dialog>>
<<narrative>>
Just then, you hear the front door open.
<</narrative>>
<<dialog "mother">>
(Looks toward the door)
Ah there they are.
(Calls out)
Welcome home sweethearts!
<</dialog>>
<div class="btn-center">
<<btn "Continue" "quest_moving_troubles_father_news">><</btn>>
</div>
</div>/* ========================================
SCENE 4: ROOM - LONELINESS
======================================== */
<div class="divnobr">
<<nobr>>
<<set $location = "fhBedroom">>
<<advanceTime 33>>
<<updateCharacterLocations>>
<<completeQuest "moving_troubles">>
<</nobr>>
<<narrative "Your Room">>
You walk into your room and close the door behind you. You lean your back against it and just stand there for a moment.
<</narrative>>
<<narrative>>
Empty room. Four walls. A bed. A desk. That's it.
<</narrative>>
<<narrative>>
You walk over to the bed and sit down.
<</narrative>>
<<dialog "player">>
(Whispers)
This city feels cursed...
<</dialog>>
<<narrative>>
Your eyes scan the room. The walls are bare. There's nothing. You have nothing anymore.
<</narrative>>
<<narrative>>
Your laptop. The photos inside, the messages, the games, the music... All gone.
<</narrative>>
<<narrative>>
Your clothes. Your favorite sweater. The necklace grandma gave you. The framed photo with your friends from your old school.
<</narrative>>
<<narrative>>
All of it. Gone.
<</narrative>>
<<narrative>>
Your throat tightens.
<</narrative>>
<<dialog "player">>
(Voice trembling)
...
<</dialog>>
<<narrative>>
You lie down on the bed. You pull the pillow to your chest and hold it tight.
<</narrative>>
<<narrative>>
Your eyes are burning. You try to hold it back but...
<</narrative>>
<<narrative>>
The first tear falls onto the pillow.
<</narrative>>
<<narrative>>
Then the second.
<</narrative>>
<<narrative>>
You bite your lip. You try not to make a sound. You can hear your family talking downstairs.
<</narrative>>
<<dialog "player">>
(Muffled, to yourself)
Why... why did everything turn out like this...
<</dialog>>
<<narrative>>
You bury your face in the pillow. Your shoulders are shaking.
<</narrative>>
<<narrative>>
Your old room comes to mind. The posters on the walls, the books on the shelves, the mess on the desk... Everything had its place. Everything was yours.
<</narrative>>
<<narrative>>
And now...
<</narrative>>
<<narrative>>
Nothing.
<</narrative>>
<<narrative>>
The tears won't stop. You give up trying to hold them back. No one's watching anyway.
<</narrative>>
<<narrative>>
Minutes pass. Maybe more. You don't know.
<</narrative>>
<<narrative>>
Eventually the tears dry up. Your eyes are puffy, your face is wet. But the weight inside feels a little lighter somehow.
<</narrative>>
<<narrative>>
You stare at the ceiling. You can hear mom downstairs trying not to make noise.
<</narrative>>
<<narrative>>
You get up and go to the bathroom. You splash cold water on your face. Your eyes are red in the mirror but... better.
<</narrative>>
<<narrative>>
You go back to your room.
<</narrative>>
<div class="btn-center">
<<btn "..." "fhBedroom">><</btn>>
</div>
</div>/* ========================================
SCENE 1: WALKING HOME - STRANGE SOUNDS
======================================== */
<div class="divnobr">
<<nobr>>
<<set $location = "maplewood">>
<<updateCharacterLocations>>
<<discover "SunsetPark" "Sunset Park">>
<</nobr>>
<<narrative "Walking Through the Neighborhood">>
You pause. Something's coming from nearby - music, laughter, children's voices.
<</narrative>>
<<dialog "player">>
(Inner voice)
Where's that coming from?
<</dialog>>
<<narrative>>
You decide to take the back streets instead of the main road. Might as well see the neighborhood a bit.
Narrow streets, houses with gardens, parked cars... Typical neighborhood stuff.
A few minutes later, you spot the source of the sounds. On your left, there's a wide park - trees, benches, people sitting on the grass, kids running around the playground.
<</narrative>>
<<image "assets/content/locations/maplewood/sunsetPark.webp" "100%">>
<<dialog "player">>
(Inner voice)
Nice place.
<</dialog>>
<<narrative>>
You check the brochure. "Sunset Park" it says.
<</narrative>>
<<dialog "player">>
(Inner voice)
I'll come back sometime.
<</dialog>>
<<narrative>>
You walk past the park and continue home. Mom's waiting for the groceries.
<</narrative>>
<div class="btn-center">
<<btn "Head Home" "fhKitchen">>
<<advanceQuestStage "moving_troubles">>
<</btn>>
</div>
</div>/* ========================================
SCENE: FAMILY DINNER
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhKitchen">>
<<updateCharacterLocations>>
<<eatFood 750 100>>
<<loseStat "thirst" 100>>
<<gainStat "energy" 45>>
<<loseStat "stress" 10>>
<<earnMoney 100>>
<<set $familyMeals.dinner = true>>
<<gainCharacterStat "mother" "friendship" 2>>
<<gainCharacterStat "mother" "love" 1>>
<<gainCharacterStat "father" "friendship" 2>>
<<gainCharacterStat "father" "love" 1>>
<<gainCharacterStat "brother" "friendship" 2>>
<<gainCharacterStat "brother" "love" 1>>
<<advanceTime 30 "relax">>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative "Kitchen - Evening">>
You sit down at the table quietly.
<</narrative>>
<<narrative>>
Mom puts food on your plate.
<</narrative>>
<<dialog "father">>
(Looks at you)
I'm sorry sweetie. I didn't want it to be like this.
<</dialog>>
<<dialog "player">>
(Shrugs)
Nothing we can do about it now.
<</dialog>>
<<dialog "mother">>
(Reaches out and holds your hand)
Don't worry, we'll figure it out.
<</dialog>>
<<narrative>>
Mom gets up and goes to another room. A minute later she comes back with some cash in her hand. She puts the money in front of you.
<</narrative>>
<<dialog "player">>
Mom, you don't have to...
<</dialog>>
<<dialog "mother">>
(Smiles)
Shh, it's fine. I'm your mom, don't argue. Get yourself something.
<</dialog>>
<<dialog "player">>
(Takes the money)
Okay. But where would I even go? I don't know the city yet.
<</dialog>>
<<dialog "mother">>
(Smiling)
I'm sure you'll find some places. Go out a bit, walk around, explore the city.
<</dialog>>
<<dialog "player">>
I don't even know what stores are out there...
<</dialog>>
<<dialog "father">>
Look it up online first.
<</dialog>>
<<dialog "player">>
I don't have a computer, dad.
<</dialog>>
<<dialog "mother">>
(Looks at your brother)
Use your brother's.
<</dialog>>
<<narrative>>
You look at your brother.
<</narrative>>
<<dialog "brother">>
(Without looking up from his food)
Sure, no problem.
<</dialog>>
<<narrative>>
You pause for a moment.
<</narrative>>
<<dialog "player">>
I want to say something by the way.
<</dialog>>
<<dialog "father">>
Hm?
<</dialog>>
<<dialog "player">>
I want to get a job.
<</dialog>>
<<dialog "brother">>
(Looks up)
Huh?
<</dialog>>
<<dialog "player">>
It's obvious we're tight on money. I don't want to be a burden.
<</dialog>>
<<dialog "father">>
You're not a burden, what are you talking about?
<</dialog>>
<<dialog "player">>
I know but... still. Better than sitting around doing nothing.
<</dialog>>
<<dialog "mother">>
(Smiles)
It's up to you sweetie. Whatever you want.
<</dialog>>
<<dialog "father">>
(Nods)
Actually I think it's a good idea. You'll get to know the city, meet people.
<</dialog>>
<<dialog "brother">>
(Grinning)
What are you gonna buy me with your first paycheck?
<</dialog>>
<<dialog "player">>
For you?
<</dialog>>
<<dialog "brother">>
Of course for me. I'll write you a list.
<</dialog>>
<<dialog "player">>
(Chuckles)
Alright, deal.
<</dialog>>
<<narrative>>
A light laugh around the table. For the first time today, the mood softens a little.
<</narrative>>
<<nobr>>
<<completeQuest "new_beginnings">>
<</nobr>>
<div class="btn-center">
<<btn "Get up from the table" "fhLivingroom">><</btn>>
</div>
</div>/* ========================================
SCENE: USE COMPUTER - START
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhBrotherRoomPC">>
<<updateCharacterLocations>>
<</silently>>
<<narrative "Brother's Room">>
You sit down at your brother's desk. As messy as usual - empty energy drink cans, crumpled papers, gaming magazines scattered about.
You power on the computer. The monitor flickers with a bluish light. The desktop loads - a sexy anime character wallpaper. You're a bit surprised, but that's not what you're curious about right now. Folders everywhere, ridiculous file names, game shortcuts. The desktop is just as messy as his room.
<</narrative>>
<<image "assets/content/scenes/maplewood/familyHouse/brotherRoom/brotherPC.webp" "100%">>
<<dialog "player">>
(Inner voice)
For a moment, I wonder what's in those folders. I glance at the door... but my mind is elsewhere, so I let it go.
<</dialog>>
<<narrative>>
You open the browser and type "city guide" into the search bar.
First result: "CityGuide.com - Everything About Your New Home"
You click on it. The page loads - city skyline at sunset, welcoming text:
<<image "assets/content/scenes/maplewood/familyHouse/brotherRoom/questComputer/browser_cityguide.webp" "100%">>
"From a small railway town founded in 1889 to a diverse city of 200,000 today... From historic districts to modern downtown, from quiet suburbs to vibrant university life - there's something here for everyone."
You see the navigation links:
About the City
Districts & Neighborhoods
Services & Amenities
Community Board
You click on "Districts & Neighborhoods."
<</narrative>>
<div class="btn-center">
<<btn "Continue" "quest_use_computer_02_district_map">><</btn>>
</div>
</div>/* ========================================
SCENE: USE COMPUTER - DISTRICT MAP
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhBrotherRoomPC">>
<</silently>>
<<narrative "Brother's Computer">>
A stylized map appears the city divided into colored zones. Your cursor hovers over each area, revealing popup descriptions: Maplewood, residential, tree-lined, family-friendly. Downtown, business district, skyscrapers, always busy. Hillcrest, upscale neighborhood, expensive homes, golf courses. University District, college campus, student housing, cafes. Old Town, historic center, civic services, main street shops.
<<image "assets/content/scenes/maplewood/familyHouse/brotherRoom/questComputer/browser_district_map.webp" "100%">>
Your cursor stops on Old Town. The "civic services" part catches your attention. You click.
The page loads with vintage photographs: old brick buildings, cobblestone streets, black and white images from decades past. "Old Town is the historic heart of our city. Originally the main business district, it now serves as the civic and cultural center."
<<image "assets/content/scenes/maplewood/familyHouse/brotherRoom/questComputer/browser_old_town.webp" "100%">>
You keep scrolling. Key locations: Civic Center Town Hall (City offices, Career Services), Maplewood High School, Public Library, Post Office. Historic Main Street local shops, Ruby's Diner (Est. 1952). Community Spaces Old Town Square, Heritage Museum.
Your eyes linger on "Town Hall - Career Services."
<</narrative>>
<<dialog "player">>
(Inner voice)
That's where I live now. Downtown looks huge. Hillcrest's the rich partwhatever. University... might be useful if I go back to college. So everything's here. Career Services... like the yellow pages or something. This could be useful.
<</dialog>>
<div class="btn-center">
<<btn "Continue" "quest_use_computer_03_career_center">><</btn>>
</div>
</div>/* ========================================
SCENE: USE COMPUTER - CAREER CENTER
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhBrotherRoomPC">>
<</silently>>
<<narrative "Brother's Computer">>
You click on the "Career Services" link.
A new page opens. A photo of Town Hall - a classic building with white columns, official-looking.
<<image "assets/content/scenes/maplewood/familyHouse/brotherRoom/questComputer/browser_career_center.webp" "100%">>
Heading: "Town Hall - Career Services Center"
Below it, a description:
"Our city's official job placement and career counseling center. We offer free services to job-seeking citizens:
Resume preparation assistance
Job application counseling
Local employer connections
Vocational training programs"
Hours: Monday - Friday, 09:00 - 17:00
Address: Old Town, Civic Center
<</narrative>>
<<dialog "player">>
(Inner voice)
I should go there. It's an official place, I'll get professional help. I'll stop by tomorrow.
<</dialog>>
<<narrative>>
You've gathered enough information. You close the browser
Just then, the door opens.
<</narrative>>
<div class="btn-center">
<<btn "Continue" "quest_use_computer_04_brother_enters">><</btn>>
</div>
</div>/* ========================================
SCENE: USE COMPUTER - BROTHER ENTERS
======================================== */
<div class="divnobr">
<<silently>>
<<set $location = "fhBrotherRoomPC">>
<<advanceTime 20>>
/* Relationship bonus */
<<gainCharacterStat "brother" "friendship" 3>>
<</silently>>
<<narrative "Brother's Room">>
Your brother walks in, looking at his phone. He looks up when he sees you at the desk.
<</narrative>>
<<image "assets/content/scenes/maplewood/familyHouse/brotherRoom/questComputer/brother_enters.webp" "100%">>
<<dialog "brother">>
Oh, you done?
<</dialog>>
<<dialog "player">>
Yeah, just looked up some stuff about the city.
<</dialog>>
<<dialog "brother">>
(Leans against the desk)
Find anything good?
<</dialog>>
<<dialog "player">>
There's a career center at Town Hall. I'll probably check it out tomorrow.
<</dialog>>
<<dialog "brother">>
Town Hall? I think I've seen it. My school should be around there.
<</dialog>>
<<dialog "player">>
(Shrugs)
I don't know, it's only been a few days since we got here. Haven't explored much yet.
<</dialog>>
<<dialog "brother">>
Actually, I think it's close. You'll find it easily. Makes sense to go there. But... I think there was a help wanted sign at the Corner Shop. Saw it yesterday when I walked by.
<</dialog>>
<<dialog "player">>
Corner Shop? The one around our corner?
<</dialog>>
<<dialog "brother">>
Yeah. I think they're looking for someone.
<</dialog>>
<<narrative>>
You think about it. The Corner Shop is closer, simpler. Maybe it's a good place to start.
<</narrative>>
<<dialog "player">>
Okay, I'll check there first then. If that doesn't work out, I'll head to Town Hall.
<</dialog>>
<<dialog "brother">>
(Smiles)
Anyway, if you're done, I'll take over the computer. You know, last days of summer break. Trying to enjoy them.
<</dialog>>
<<narrative>>
You stand up from the desk.
<</narrative>>
<<dialog "player">>
(Laughing)
You've trashed your room. Clean this place up sometime. Your desktop looks just as messy as your room.
<</dialog>>
<<narrative>>
Your brother quickly sits down in the chair you just left and clicks on a game.
<</narrative>>
<<dialog "brother">>
By the way, you can use my messy computer anytime, sis. Just not when I'm gaming.
<</dialog>>
<<dialog "player">>
(Laughing)
Okay, thanks kiddo.
<</dialog>>
<<dialog "brother">>
Good morning, and in case I don't see ya, good afternoon, good evening, and good night!
<</dialog>>
<<narrative>>
You slowly walk out of the room. The plan is clear in your head: Check the Corner Shop first, then Town Hall if needed.
Time to find a job.
<</narrative>>
<<nobr>>
<<completeQuest "use_computer">>
<<set $used_computer = true>>
<<if $questState.active.find_job && $questState.active.find_job.stage === 0>><<advanceQuestStage "find_job" "check_corner_shop">><</if>>
<</nobr>>
<div class="btn-center">
<<btn "Leave the room" "fhUpperstairs">><</btn>>
</div>
</div><<nobr>>
<<set $location = "fhBrotherRoom">>
<<updateCharacterLocations>>
<<narrative "Brother's Room">>
The door's half open.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/day3/day3brother.webp" "100%">>
<<narrative>>
He's at the PC, headphones on, clicking away. He doesn't notice you.
<</narrative>>
<<narrative>>
You knock on the doorframe.
<</narrative>>
<<narrative>>
Nothing.
<</narrative>>
<<narrative>>
You knock harder.
<</narrative>>
<<dialog "brother">>
What.
<</dialog>>
<<narrative>>
He doesn't even look. Keeps clicking.
<</narrative>>
<<dialog "player">>
Can I come in?
<</dialog>>
<<dialog "brother">>
You're already in.
<</dialog>>
<<narrative>>
You sit on the edge of his bed. Watch him play for a while. Just keyboard and mouse.
<</narrative>>
<<dialog "player">>
Can I ask you something?
<</dialog>>
<<dialog "brother">>
Mm.
<</dialog>>
<<dialog "player">>
Someone said something to me today.
<</dialog>>
<<dialog "brother">>
Okay?
<</dialog>>
<<dialog "player">>
About how I look.
<</dialog>>
<<narrative>>
Still not looking at you.
<</narrative>>
<<dialog "brother">>
Who? Your boyfriend?
<</dialog>>
<<dialog "player">>
Shut up.
<</dialog>>
<<narrative>>
Your face goes hot. Why did you say that, you think.
<</narrative>>
<<narrative>>
He swivels his chair halfway. Lifts one side of his headphones.
<</narrative>>
<<narrative>>
Grins.
<</narrative>>
<<dialog "brother">>
Whoa, whoa, whoa. When's the last time you even had a boyfriend? Let me think…
<</dialog>>
<<narrative>>
He props his chin on his hand. Looks at the ceiling.
<</narrative>>
<<dialog "brother">>
Hmm…
<</dialog>>
<<dialog "brother">>
Oh, right. Never.
<</dialog>>
<<narrative>>
He leans back in the chair and laughs.
<</narrative>>
<<dialog "player">>
You're such an idiot.
<</dialog>>
<<narrative>>
You stand up.
<</narrative>>
<<dialog "brother">>
I'm joking -
<</dialog>>
<<dialog "player">>
Forget it.
<</dialog>>
<<narrative>>
You're already at the door.
<</narrative>>
<<dialog "brother">>
Hey.
<</dialog>>
<<narrative>>
Something's different in his voice. You stop, but you don't turn around.
<</narrative>>
<<narrative>>
A silence. The clicking has stopped.
<</narrative>>
<<narrative>>
You hear him pull the headphones off. Set them on the desk.
<</narrative>>
<<narrative>>
Your hand's on the doorframe.
<</narrative>>
<<dialog "brother">>
What happened?
<</dialog>>
<<narrative>>
You turn halfway. He's not laughing anymore. He's looking at you, really looking.
<</narrative>>
<<dialog "brother">>
You're my little sister. If something was wrong with your face, I'd tell you.
<</dialog>>
<<narrative>>
Half a smirk.
<</narrative>>
<<dialog "brother">>
Trust me. I wouldn't go easy.
<</dialog>>
<<dialog "player">>
…
<</dialog>>
<<dialog "player">>
This is your nice version?
<</dialog>>
<<dialog "brother">>
My honest version.
<</dialog>>
<<narrative>>
He turns back to the screen. Puts the headphones on. Then, lifts one side again.
<</narrative>>
<<dialog "brother">>
Oh, and the reason you don't have a boyfriend isn't your face. It's because you're weird.
<</dialog>>
<<narrative>>
He says it to the monitor.
<</narrative>>
<<narrative>>
The headphones go back on. Clicking starts again.
<</narrative>>
<<narrative>>
You stand there a second. Then you leave.
<</narrative>>
<<narrative>>
You're almost smiling.
<</narrative>>
<div class="location-actions">
<<btn "Continue" "fhBrotherRoom">>
<<silently>>
<<set $flags.vinceDay3FamilyBrotherDone = true>>
<<gainStat "willpower" 1>>
<<gainStat "mood" 2>>
<<gainStat "friendship" 2 "brother">>
<<gainStat "trust" 1 "brother">>
<<gainStat "love" 1 "brother">>
<<completeObjective "vince_day3_family" "talkBrother">>
<<advanceTime 7 "relax">>
<</silently>>
<</btn>>
</div>
<</nobr>>
<<nobr>>
<<set $location = "fhGarage">>
<<updateCharacterLocations>>
<<narrative "Garage">>
Oil and metal own the air in the garage.
<</narrative>>
<<narrative>>
Your dad's at the bench with the engine.
<</narrative>>
<<narrative>>
When he sees you, he looks up.
<</narrative>>
<<dialog "father">>
That you? For a second I thought you were your mom.
<</dialog>>
<<dialog "father">>
(he holds out his hand)
Pass me that wrench.
<</dialog>>
<<dialog "father">>
(you pass it to him)
Thanks.
<</dialog>>
<<narrative>>
He fits it to the part.
<</narrative>>
<<narrative>>
For a while there's only the sound of metal.
<</narrative>>
<<dialog "father">>
What'd you do today?
<</dialog>>
<<dialog "player">>
Nothing… got bored a little.
<</dialog>>
<<dialog "father">>
Hm.
<</dialog>>
<<narrative>>
He gives you a quick glance, then goes back to it.
<</narrative>>
<<dialog "father">>
How was work?
<</dialog>>
<<dialog "player">>
Fine.
<</dialog>>
<<narrative>>
A short silence.
<</narrative>>
<<narrative>>
You lean against the bench.
<</narrative>>
<<narrative>>
You scratch at a grease smear with your nail.
<</narrative>>
<<dialog "player">>
It's just -
(you stop)
<</dialog>>
<<dialog "player">>
Stupid. Never mind.
<</dialog>>
<<dialog "father">>
Say it.
<</dialog>>
<<dialog "player">>
Nothing.
<</dialog>>
<<dialog "player">>
…
<</dialog>>
<<dialog "player">>
I mean…
(you breathe in)
<</dialog>>
<<dialog "player">>
Do you think I'm -
(quieter)
<</dialog>>
<<dialog "player">>
…pretty?
<</dialog>>
<<dialog "player">>
The last word barely makes it out.
It's not a big deal. I just -
<</dialog>>
<<narrative>>
He stops.
<</narrative>>
<<narrative>>
He sets the tool down on the bench, slow.
<</narrative>>
<<narrative>>
He looks at you, but doesn't answer right away.
<</narrative>>
<<narrative>>
Then he turns back to the engine, tightens a bolt, and lets it go.
<</narrative>>
<<dialog "father">>
When I first met your mom… she was wearing this red coat, and I thought -
<</dialog>>
<<narrative>>
He rubs his brow with an oily thumb.
<</narrative>>
<<dialog "father">>
Never mind. Forget it.
<</dialog>>
<<dialog "father">>
You got your looks from your mom.
<</dialog>>
<<dialog "father">>
And she's the most beautiful woman in the world.
<</dialog>>
<<dialog "father">>
(he gives a small shrug)
The math checks out.
<</dialog>>
<<narrative>>
He steps closer and puts a hand on your shoulder.
<</narrative>>
<<narrative>>
Oily fingerprints stay on your shirt.
<</narrative>>
<<image "assets/content/scenes/oldtown/rubysDiner/day3/day3father.webp" "100%">>
<<dialog "father">>
Don't get stuck on it.
<</dialog>>
<<narrative>>
He squeezes your shoulder.
<</narrative>>
<<narrative>>
Then a clumsy, quick hug, garage and oil.
<</narrative>>
<<narrative>>
He pulls back almost at once.
<</narrative>>
<<narrative>>
For a beat neither of you knows what to do.
<</narrative>>
<<narrative>>
He fake-coughs, turns to the bench, picks up a tool, then puts the same one back down.
<</narrative>>
<<dialog "player">>
…
<</dialog>>
<<dialog "player">>
Thanks, Dad.
<</dialog>>
<<dialog "father">>
Hm.
<</dialog>>
<<narrative>>
He bends over the engine again.
For a long moment his hands don't move.
<</narrative>>
<div class="location-actions">
<<btn "Continue" "fhGarage">>
<<silently>>
<<set $flags.vinceDay3FamilyFatherDone = true>>
<<gainStat "willpower" 1>>
<<gainStat "mood" 2>>
<<gainStat "friendship" 2 "father">>
<<gainStat "trust" 1 "father">>
<<gainStat "love" 1 "father">>
<<completeObjective "vince_day3_family" "talkFather">>
<<advanceTime 10 "relax">>
<</silently>>
<</btn>>
</div>
<</nobr>>
<<nobr>>
<<set $location = "fhKitchen">>
<<updateCharacterLocations>>
<<image "assets/content/scenes/oldtown/rubysDiner/day3/day3mother.webp" "100%">>
<<narrative "Kitchen">>
The kitchen is warm. She's busy at the counter.
You stand in the doorway for a beat, trying to line up what you want to say.
<</narrative>>
<<dialog "player">>
Mom…
<</dialog>>
<<dialog "mother">>
Yes, honey?
<</dialog>>
<<dialog "player">>
Do you think I'm… pretty?
<</dialog>>
<<narrative>>
She looks up. The question hangs there.
Surprise flickers across her face, then it softens.
<</narrative>>
<<dialog "mother">>
Where is this coming from?
<</dialog>>
<<dialog "player">>
I don't know.
(you look away)
<</dialog>>
<<dialog "player">>
It just got stuck in my head.
<</dialog>>
<<narrative>>
She turns toward you. This time she really looks, studies you. She's not rushing.
<</narrative>>
<<dialog "mother">>
Yes.
<</dialog>>
<<dialog "mother">>
You're beautiful.
<</dialog>>
<<dialog "mother">>
(she steps closer)
But I don't think that's what this is about.
<</dialog>>
<<dialog "player">>
What do you mean?
<</dialog>>
<<dialog "mother">>
Sometimes you can know you're beautiful… and still not feel it.
<</dialog>>
<<dialog "mother">>
Is today one of those days?
<</dialog>>
<<dialog "player">>
Maybe.
<</dialog>>
<<dialog "mother">>
Come here.
(she touches your shoulder, light)
<</dialog>>
<<image "assets/content/scenes/oldtown/rubysDiner/day3/day3mother.webp" "100%">>
<<dialog "mother">>
Don't look at yourself like that.
(softer now)
<</dialog>>
<<dialog "mother">>
There's nothing wrong with you.
<</dialog>>
<<dialog "mother">>
If this comes up again, you might ask a different question next time - and that's all right. I'll still be here for it.
<</dialog>>
<<dialog "player">>
Okay…
(you nod a little)
<</dialog>>
<<dialog "player">>
Thank you.
<</dialog>>
<<dialog "mother">>
Always.
<</dialog>>
<div class="location-actions">
<<btn "Continue" "fhKitchen">>
<<silently>>
<<set $flags.vinceDay3FamilyMomDone = true>>
<<gainStat "willpower" 1>>
<<gainStat "mood" 2>>
<<gainStat "friendship" 2 "mother">>
<<gainStat "trust" 1 "mother">>
<<gainStat "love" 1 "mother">>
<<completeObjective "vince_day3_family" "talkMother">>
<<advanceTime 10 "relax">>
<</silently>>
<</btn>>
</div>
<</nobr>>
<<nobr>>
<<set $location = "fhBedroom">>
<<updateCharacterLocations>>
<<narrative "Bedroom">>
You close the door. The room goes quiet.
<</narrative>>
<<narrative>>
You sit on the bed. Stay like that for a while.
<</narrative>>
<<narrative>>
Your eyes snag on the mirror across the room.
<</narrative>>
<<narrative>>
You don't get up and go to it.
<</narrative>>
<<narrative>>
You just look from the bed. From a distance.
<</narrative>>
<<narrative>>
It comes back to you.
<</narrative>>
<<narrative>>
Do you ever look at yourself in the mirror?
<</narrative>>
<<dialog "player">>
(Inner voice)
I still hear him asking that.
<</dialog>>
<<narrative>>
That day your face had burned. You'd looked away. You couldn't answer.
<</narrative>>
<<dialog "player">>
(Inner voice)
But I'm thinking about it now.
<</dialog>>
<<narrative>>
Your mom looked at you. Really looked.
<</narrative>>
<<narrative>>
And she sounded sure.
<</narrative>>
<<narrative>>
Your dad couldn't finish the sentence, but he didn't have to.
<</narrative>>
<<narrative>>
Your brother made fun of you. Then he took his headphones off.
<</narrative>>
<<narrative>>
He never takes his headphones off for anything.
<</narrative>>
<<narrative>>
You smile. A little.
<</narrative>>
<<narrative>>
But then the thought comes.
<</narrative>>
<<dialog "player">>
(Inner voice)
They're my family.
Of course they'll say that.
They don't want to hurt me. They won't make me cry.
<</dialog>>
<<dialog "player">>
(Inner voice)
They won't look me in the eye and say yes, something's wrong.
That's what family does. They have to.
<</dialog>>
<<narrative>>
Vince wasn't family. You barely knew him.
<</narrative>>
<<narrative>>
And he looked at you. And he said what he saw.
<</narrative>>
<<dialog "player">>
(Inner voice)
So what about everyone else?
Maybe that's what everyone on the outside sees too.
Maybe my family just isn't saying it.
<</dialog>>
<<narrative>>
…
<</narrative>>
<<narrative>>
You sit with that for a while.
<</narrative>>
<<narrative>>
Then you stand up. You walk to the mirror.
<</narrative>>
<<narrative>>
You look.
<</narrative>>
<<narrative>>
Your hair's a mess. The skin under your eyes is a little tired.
<</narrative>>
<<narrative>>
Your dad's oily fingerprints are still on your shirt.
<</narrative>>
<<dialog "player">>
(Inner voice)
I'm not perfect. I know that.
<</dialog>>
<<narrative>>
But…
<</narrative>>
<<dialog "player">>
(Inner voice)
My brother said it. If something was wrong with my face, he'd tell me. He wouldn't go easy.
He wouldn't. He really wouldn't.
And he didn't say it.
<</dialog>>
<<narrative>>
This time you don't look away. You keep looking.
<</narrative>>
<<dialog "player">>
(Inner voice)
Vince's voice is still there somewhere. It doesn't fully leave.
But it's not as loud as it used to be.
<</dialog>>
<div class="location-actions">
<<btn "Continue" "fhBedroom">>
<<silently>>
<<loseStat "mood" 5>>
<<set $flags.vinceDay3FamilyBedroomDone = true>>
<<advanceQuestStage "vince_day3_family">>
<<advanceTime 8 "relax">>
<</silently>>
<</btn>>
</div>
<</nobr>>
<div class="divnobr">
<<set _backTo = $readReturnPassage || "Read">>
<<set $location = setup.resolveReadingLocationId(_backTo)>>
<<if !$readSelectedItem>>
<<run window.notifyWarning("Please select a book or magazine first.")>>
<<goto "Read">>
<</if>>
<<set _meta = setup.getReadingItem($readSelectedItem)>>
<<if !_meta>>
<<run window.notifyWarning("Invalid selection.")>>
<<goto "Read">>
<</if>>
/* Hard lock: check stat requirements before allowing read */
<<if _meta.requires>>
<<set _reqFail = false>>
<<if _meta.requires.intelligence && ($intelligence || 0) < _meta.requires.intelligence>><<set _reqFail = true>><</if>>
<<if _meta.requires.focus && ($focus || 0) < _meta.requires.focus>><<set _reqFail = true>><</if>>
<<if _reqFail>>
<<run window.notifyWarning("Your stats are too low to read this book.")>>
<<goto "Read">>
<</if>>
<</if>>
<<silently>>
<<if ndef $daily.magazineStatUsedToday>><<set $daily.magazineStatUsedToday = false>><</if>>
<<if ndef $daily.bookMinutesRead>><<set $daily.bookMinutesRead = 0>><</if>>
/* Duration from Read UI (manual picker) or fallback to btnPicker memory */
<<if def $readDurationChoice>>
<<set $selectedDuration = $readDurationChoice>>
<<elseif def $pickerMemory and def $pickerMemory.readingDuration>>
<<set $selectedDuration = $pickerMemory.readingDuration>>
<<else>>
<<set $selectedDuration = 15>>
<</if>>
<<set $selectedDuration = Math.min(60, Math.max(15, parseInt($selectedDuration) || 15))>>
<<set _int = $intelligence || 0>>
<<set _foc = $focus || 0>>
<<set _wil = $willpower || 0>>
<<set _pagesPerMin = setup.calcReadingSpeed(_int, _foc, _wil)>>
<<set _minutes = $selectedDuration>>
<<set _rawPages = Math.floor(_pagesPerMin * _minutes)>>
<<if _meta.type === "book">>
<<set _startPage = $readProgress[$readSelectedItem] || 0>>
<<set _remaining = _meta.pages - _startPage>>
<<set _pagesRead = Math.min(_rawPages, _remaining)>>
<<set _newPage = _startPage + _pagesRead>>
<<else>>
/* Magazine: single use one session = read & discard; pages are flavor only */
<<set _pagesRead = Math.min(_rawPages, _meta.pages)>>
<<set _newPage = _pagesRead>>
<</if>>
/* Book daily limit: 2 hours (120 minutes) */
<<set _bookDailyLimit = 120>>
<<set _bookLimitReached = (_meta.type === "book" && ($daily.bookMinutesRead || 0) >= _bookDailyLimit)>>
<<set _magSkim = false>>
<<set _magCapSkim = false>>
<<if _meta.type === "magazine">>
<<set _sgMeta = _meta.skillOnComplete>>
<<if _sgMeta && _sgMeta.category && _sgMeta.skill>>
<<set _skillCapMeta = _meta.skillMaxCap || 30>>
<<set _curSkillMeta = 0>>
<<if $skills && $skills[_sgMeta.category] && def $skills[_sgMeta.category][_sgMeta.skill]>>
<<set _curSkillMeta = $skills[_sgMeta.category][_sgMeta.skill]>>
<</if>>
<<if _curSkillMeta >= _skillCapMeta>>
<<set _magCapSkim = true>>
<</if>>
<</if>>
<</if>>
<<if _meta.type === "magazine" && ($daily.magazineStatUsedToday || _magCapSkim)>>
<<set _magSkim = true>>
<</if>>
<<set _totalGain = 0>>
<<set _focusGain = 0>>
<<set _gainTypeAwarded = "">>
<<if _meta.type === "book">>
<<if !_bookLimitReached>>
/* Raw gain from pages read this session */
<<set _gainPerPage = _meta.statGainPerPage || 0.02>>
<<set _rawGain = _pagesRead * _gainPerPage>>
/* Pick 1 or 2 stats randomly from statPool */
<<set _pool = (_meta.statPool && _meta.statPool.length > 0) ? _meta.statPool : ["intelligence"]>>
<<set _numStats = Math.min(random(1, 2), _pool.length)>>
<<set _shuffled = _pool.slice().sort(function(){ return Math.random() - 0.5; })>>
<<set _selectedStats = _shuffled.slice(0, _numStats)>>
/* Distribute gain across selected stats */
<<set _gainTypeAwarded = _selectedStats.join("+")>>
<<set _totalGain = 0>>
<<if _numStats === 1>>
<<if _rawGain > 0>>
<<gainStat _selectedStats[0] _rawGain>>
<<set _totalGain = _rawGain>>
<</if>>
<<else>>
/* Random 40–60 split between the two */
<<set _splitRatio = 0.4 + (Math.random() * 0.2)>>
<<set _gainA = _rawGain * _splitRatio>>
<<set _gainB = _rawGain - _gainA>>
<<if _gainA > 0>><<gainStat _selectedStats[0] _gainA>><</if>>
<<if _gainB > 0>><<gainStat _selectedStats[1] _gainB>><</if>>
<<set _totalGain = _rawGain>>
<</if>>
/* Focus bonus: concentration from reading any book */
<<set _focusGain = _pagesRead * 0.03>>
<<if _focusGain > 0>>
<<gainStat "focus" _focusGain>>
<</if>>
/* Skill gain: per page, no cap; skillOnRead is an array */
<<if _meta.skillOnRead && _meta.skillOnRead.length > 0>>
<<for _sr range _meta.skillOnRead>>
<<set _srGain = _pagesRead * (_sr.gainPerPage || 0.025)>>
<<if _srGain > 0>>
<<gainSkill _sr.category _sr.skill _srGain 9999>>
<</if>>
<</for>>
<</if>>
/* Passive book effects: mood up, stress down, energy cost (floor: 10) */
<<set _bookMoodGain = Math.round(_minutes * 0.12)>>
<<set _bookStressLoss = Math.round(_minutes * 0.1)>>
<<set _bookEnergyCost = Math.round(_minutes * 0.2)>>
<<if _bookMoodGain > 0>><<gainStat "mood" _bookMoodGain>><</if>>
<<if _bookStressLoss > 0>><<loseStat "stress" _bookStressLoss>><</if>>
<<set $energy = Math.max(10, ($energy || 0) - _bookEnergyCost)>>
/* Track today's reading minutes */
<<set $daily.bookMinutesRead = ($daily.bookMinutesRead || 0) + _minutes>>
<</if>>
<<else>>
/* Base magazine effects: always apply on read */
<<gainStat "mood" 5>>
<<loseStat "stress" 5>>
/* Magazine: first issue of the day optional gainStat + optional skillOnComplete (gainSkill); skim after */
<<set _magRewarded = false>>
<<if !_magSkim>>
<<set _gc = _meta.gainOnComplete || 0>>
<<set _magGainType = _meta.gainType || "">>
<<if _meta.gainTypeOptions && _meta.gainTypeOptions.length > 0>>
<<set _idx = random(1, _meta.gainTypeOptions.length) - 1>>
<<set _magGainType = _meta.gainTypeOptions[_idx]>>
<</if>>
<<if _gc > 0 && _magGainType>>
<<set _applyGain = _gc>>
/* Cap only applies to core stats (not vitals like mood/stress/energy/health) */
<<set _cappedStats = ["intelligence", "focus", "creativity", "willpower", "charisma"]>>
<<if _cappedStats.includes(_magGainType)>>
<<set _currentMagStat = State.variables[_magGainType] || 0>>
<<if _currentMagStat >= 20>>
<<set _applyGain = 0>>
<</if>>
<</if>>
<<if _applyGain > 0>>
<<gainStat _magGainType _applyGain>>
<<set _totalGain = _applyGain>>
<<set _gainTypeAwarded = _magGainType>>
<<set _magRewarded = true>>
<</if>>
<</if>>
<<set _sg = _meta.skillOnComplete>>
<<if _sg && _sg.category && _sg.skill && (_sg.amount || 0) > 0>>
<<set _skillCap = _meta.skillMaxCap || 30>>
<<set _curSkill = 0>>
<<if $skills && $skills[_sg.category] && def $skills[_sg.category][_sg.skill]>>
<<set _curSkill = $skills[_sg.category][_sg.skill]>>
<</if>>
<<if _curSkill < _skillCap>>
<<gainSkill _sg.category _sg.skill _sg.amount _skillCap>>
<<set _magRewarded = true>>
<</if>>
<</if>>
<<if _magRewarded>>
<<set $daily.magazineStatUsedToday = true>>
<</if>>
<</if>>
<</if>>
<<if !_bookLimitReached>>
<<advanceTime $selectedDuration "relax">>
<</if>>
<<set State.temporary.newPage = _newPage>>
<<if _meta.type === "book" && !_bookLimitReached>>
<<if _newPage >= _meta.pages>>
<<run (function(){ var sv=State.variables,rp=sv.readProgress,id=sv.readSelectedItem; if(rp&&rp[id]!==undefined)delete rp[id]; if(!sv.readFinished)sv.readFinished=[]; sv.readFinished.push(id); })()>>
<<else>>
<<run (function(){ var sv=State.variables,rp=sv.readProgress,id=sv.readSelectedItem,np=State.temporary.newPage; if(!rp)sv.readProgress={}; rp[id]=np; })()>>
<</if>>
<<elseif _meta.type === "magazine">>
<<removeFromInventory $readSelectedItem 1>>
<<run (function(){ var rp=State.variables.readProgress, id=State.variables.readSelectedItem; if(rp&&rp[id]!==undefined)delete rp[id]; })()>>
<</if>>
<<if _meta.type === "magazine" && _magSkim>>
<<set _skimPool = _meta.skimNarratives>>
<<if !_skimPool || _skimPool.length === 0>>
<<set _skimPool = ["You flip through without really focusing. The time passes."]>>
<</if>>
<<set _narrIdx = random(1, _skimPool.length) - 1>>
<<set _narrText = _skimPool[_narrIdx]>>
<<elseif _meta.type === "book">>
<<if _bookLimitReached>>
/* Already hit 2-hour daily limit pick a "had enough for today" line */
<<set _limitPool = [
"You reach for the book and flip it open, but something stops you. Two hours of reading is probably enough for one day your eyes need a rest.",
"You pick up the book with the best of intentions, then set it back down. You've already put in a solid stretch today. Tomorrow.",
"The page is right where you left it. You almost sit down to keep going, but decide you've absorbed enough for today. The words would start blurring anyway.",
"You glance at the book, genuinely tempted. But you've been reading for hours already your brain isn't really taking it in anymore. You close it before you even start.",
"You open to your bookmarked page and stare at it for a moment. Then you slowly close the cover. You've done enough reading for today."
]>>
<<set _narrIdx = random(1, _limitPool.length) - 1>>
<<set _narrText = _limitPool[_narrIdx]>>
<<else>>
/* Position-based narrative: ratio of pages read BEFORE this session */
<<set _readRatio = _meta.pages > 0 ? (_startPage / _meta.pages) : 0>>
<<if _readRatio < 0.33>>
<<set _narrPool = _meta.earlyNarratives>>
<<elseif _readRatio < 0.66>>
<<set _narrPool = _meta.midNarratives>>
<<else>>
<<set _narrPool = _meta.lateNarratives>>
<</if>>
<<if !_narrPool || _narrPool.length === 0>>
<<set _narrPool = ["You read quietly, turning pages steadily."]>>
<</if>>
<<set _narrIdx = random(1, _narrPool.length) - 1>>
<<set _narrText = _narrPool[_narrIdx]>>
<</if>>
<<else>>
<<set _narratives = _meta.narratives || ["You read quietly for a while."]>>
<<set _narrIdx = random(1, _narratives.length) - 1>>
<<set _narrText = _narratives[_narrIdx]>>
<</if>>
<<if !_bookLimitReached>>
<<set _locationIntro = setup.getReadingLocationIntro($readReturnPassage)>>
<<set _media = setup.getReadingLocationMediaRandom($readReturnPassage, _meta.type)>>
<<if _media>>
<<set _mediaType = _media.type>>
<<set _mediaPath = _media.path>>
<<set _mediaSize = _media.size || "100%">>
<<else>>
<<set _mediaType = "">>
<<set _mediaPath = "">>
<<set _mediaSize = "100%">>
<</if>>
<<else>>
<<set _mediaType = "">>
<<set _mediaPath = "">>
<<set _mediaSize = "100%">>
<</if>>
<</silently>>
<<flushNotifications>>
<<if _bookLimitReached>>
<<narrative "Reading">>
<<print _narrText>>
<</narrative>>
<<else>>
<<narrative "Reading">>
<<print _locationIntro>>
<</narrative>>
<<if _mediaType === "vid">><<vid _mediaPath _mediaSize>><</if>>
<<if _mediaType === "image">><<image _mediaPath _mediaSize>><</if>>
<<if _meta.type === "magazine" && _magSkim>>
<<narrative _meta.name>>
<<print _narrText>>
<</narrative>>
<<narrative>>
You read for <<print $selectedDuration>> minutes mostly skimming, not really paying attention. When you're done, you toss the issue aside.
<</narrative>>
<<elseif _meta.type === "magazine">>
<<narrative _meta.name>>
<<print _narrText>>
<</narrative>>
<<narrative>>
You read for <<print $selectedDuration>> minutes and get through the issue. Done you toss it in the recycling.
<</narrative>>
<<else>>
<<narrative _meta.name>>
<<print _narrText>>
<</narrative>>
<<narrative>>
You read for <<print $selectedDuration>> minutes and get through <<print _pagesRead>> pages.
<</narrative>>
<</if>>
<</if>>
<div class="location-actions">
<<btn "Back" _backTo>><</btn>>
</div>
</div><<nobr>><<set $location = "recreationCenter">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Metro Recreation Center">>A fitness complex with courts, gym, and workout facilities. The sound of bouncing balls echoes.<</narrative>>
<<showLocationChars "recreationCenter">>
<<navMenu>>
<<navCard "courtBasketball">>
<<navCard "courtVolleyball">>
<<navCard "gym">>
<<navCard "towerA">>
<</navMenu>><<nobr>><<set $location = "redLightCenter">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Red Light Center">>The adult entertainment district. Neon lights, explicit establishments, and a sense of danger after dark.<</narrative>>
<<showLocationChars "redLightCenter">>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "alleyBack">>
<<navCard "basementEntrance">>
<<navCard "clubPrivate">>
<<navCard "motelRedLight">>
<<navCard "parlorMassage">>
<<navCard "roomsPrivate">>
<<navCard "storeAdult">>
<<navCard "marinaBay">>
<<navCard "downTown">>
<<navCard "southside">>
<</navMenu>><<nobr>><<set $location = "restaurantBeach">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Captain Jack's">>Fresh seafood with ocean views. The catch of the day is always a good choice.<</narrative>>
<<showLocationChars "restaurantBeach">>
<<navMenu>><<navCard "marinaBay">><</navMenu>><<nobr>>
<<set $location = "restaurantDowntown">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "The Brass Rail">>
An upscale restaurant with white tablecloths and attentive waiters. The aroma of gourmet cuisine fills the air.
<</narrative>>
<<showLocationChars "restaurantDowntown">>
<<navMenu>>
<<navCard "downTown">>
<</navMenu>><div class="divnobr">
<<nobr>>
<<set $location = $restaurantReturnPassage || $location || "start">>
<<set _back = $location>>
<<set _mealMinutes = random(30, 45)>>
<<set _sceneIndex = random(1, 2)>>
<<advanceTime _mealMinutes "relax">>
<<narrative "Meal">>
You enjoy your meal. The food hits the spot and you feel refreshed.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/global/eatRestaurant/eatfood1.mp4" "100%">>
<<else>>
<<vid "assets/content/scenes/global/eatRestaurant/eatfood2.mp4" "100%">>
<</if>>
<div class="location-actions">
<<btn "Back" _back>><</btn>>
</div>
<</nobr>>
</div><<nobr>><<set $location = "restaurantLuxury">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Château Noir">>Michelin-starred dining. Reservations weeks in advance, tasting menus and wine pairings.<</narrative>>
<<showLocationChars "restaurantLuxury">>
<<navMenu>><<navCard "streetFifth">><</navMenu>><<nobr>><<set $location = "restaurantRoof">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Altitude 47">>Fine dining with panoramic city views. White tablecloths and candlelight set the mood.<</narrative>>
<<showLocationChars "restaurantRoof">>
<<navMenu>><<navCard "towerC">><</navMenu>><<nobr>><<set $location = "roofTopLounge">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Cloud Nine Lounge">>An exclusive open-air lounge with stunning skyline views. Cocktails and conversation under the stars.<</narrative>>
<<showLocationChars "roofTopLounge">>
<<navMenu>><<navCard "towerC">><</navMenu>><<nobr>><<set $location = "roomsPrivate">><</nobr>>
<<updateCharacterLocations>>
<<narrative "VIP Rooms">>Rentable rooms for private encounters. Cash only, no names.<</narrative>>
<<showLocationChars "roomsPrivate">>
<<navMenu>><<navCard "redLightCenter">><</navMenu>><<silently>>
<<set _danceActRet = $activityOrigin || "fhLivingroom">>
<<requireMinEnergy 20 _danceActRet "Not enough energy.">>
<<set $location = $activityOrigin>>
<<updateCharacterLocations>>
<<set $daily.danceDone = true>>
<<advanceTime 30 "relax">>
<<loseStat "energy" 10>>
<<loseStat "stress" 15>>
<<flushNotifications>>
<<gainSkill "physical" "dance" 1 20>>
<<recalculateStats>>
/* Text variations */
<<set _variation = random(1, 3)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "You put on a dance workout video and clear some space. For the next 30 minutes, you follow along with the energetic instructors on screen, trying to keep up with their moves. By the end, you're sweating and breathless, but your mood has definitely improved.">>
<<case 2>>
<<set _sceneText = "You queue up your favorite playlist and let the music take over. No choreography, no rules - just you moving however feels right. It's messy and probably looks ridiculous, but who cares? The endorphins are worth it.">>
<<case 3>>
<<set _sceneText = "Today you try learning a new dance routine from a tutorial. It takes a few attempts to get the steps right, and you keep rewinding the tricky parts. By the end of 30 minutes, you've almost nailed the chorus. Almost.">>
<</switch>>
<</silently>>
<<nobr>>
<<narrative "Dance Session">>
_sceneText
<</narrative>>
<<image "assets/content/scenes/maplewood/familyHouse/livingroom/danceSetup.webp" "80%">>
<div class="location-actions">
<<btn "Finish" $location>><</btn>>
</div>
<</nobr>><div class="divnobr">
<<silently>>
/* 1. Set Location from Origin */
<<if def $activityOrigin>>
<<set $location = $activityOrigin>>
<<else>>
<<if ndef $location>><<set $location = "fhBedroom">><</if>>
<</if>>
/* 2. Configure Settings per Location */
<<if $location == "fhBedroom">>
<<set _returnBtn = "fhBed">>
<<set _baseSceneText = "You settle into your comfortable bed for a quick nap. The familiar surroundings and soft mattress help you drift off quickly.">>
<<set _energyGainFactor = 20>>
<<set _stressLossFactor = 15>>
<<elseif $location == "fhLivingroom">>
<<set _returnBtn = "fhCouch">>
<<set _baseSceneText = "You stretch out across the couch, finding a comfortable position. The quiet hum of the house and the soft cushions quickly pull you into a light slumber.">>
<<set _energyGainFactor = 15>>
<<set _stressLossFactor = 10>>
<<else>>
<<set _returnBtn = $location>>
<<set _baseSceneText = "You find a quiet spot to rest your eyes for a while.">>
<<set _energyGainFactor = 10>>
<<set _stressLossFactor = 5>>
<</if>>
/* 3. Nap is always allowed */
<</silently>>
/* 4. Execute Nap */
<<nobr>>
/* SUCCESS: Execute Nap */
<<silently>>
/* Get Duration from Picker Memory (set by btnPicker) */
<<if def $pickerMemory and def $pickerMemory.napDuration>>
<<set $selectedDuration = $pickerMemory.napDuration>>
<<else>>
<<set $selectedDuration = 15>>
<</if>>
/* Constraint Duration */
<<set $selectedDuration = parseInt($selectedDuration)>>
<<if $selectedDuration > 60>><<set $selectedDuration = 60>><</if>>
<<if $selectedDuration < 15>><<set $selectedDuration = 15>><</if>>
/* Scale recovery to duration (30 min = full base values from location) */
<<set _napScale = $selectedDuration / 30>>
<<set _napEnergy = Math.max(1, Math.round(_energyGainFactor * _napScale))>>
<<set _napStress = Math.max(1, Math.round(_stressLossFactor * _napScale))>>
<<set _napMood = Math.max(1, Math.round(5 * _napScale))>>
/* Apply Changes */
<<advanceTime $selectedDuration "relax">>
<<loseStat "stress" _napStress>>
<<gainStat "energy" _napEnergy>>
<<gainStat "mood" _napMood>>
<<flushNotifications>>
<<recalculateStats>>
<<set _resultText = "After " + Math.floor($selectedDuration) + " minutes, you wake up feeling refreshed.">>
<<if $location == "fhLivingroom">>
<<set _napSceneIndex = random(1, 3)>>
<</if>>
<</silently>>
<<narrative "Taking a Nap">>
<<print _baseSceneText>>
<</narrative>>
<<if $location == "fhLivingroom">>
<<if _napSceneIndex == 1>>
<<vid "assets/content/scenes/maplewood/familyHouse/livingroom/nap/napLivingroom1.mp4" "1000%">>
<<elseif _napSceneIndex == 2>>
<<vid "assets/content/scenes/maplewood/familyHouse/livingroom/nap/napLivingroom2.mp4" "1000%">>
<<else>>
<<vid "assets/content/scenes/maplewood/familyHouse/livingroom/nap/napLivingroom3.mp4" "1000%">>
<</if>>
<</if>>
<<narrative>>
<<print _resultText>>
<</narrative>>
<div class="location-actions">
<<btn "Wake Up" _returnBtn>><</btn>>
</div>
<</nobr>>
</div><div class="divnobr">
<<silently>>
/* Set location from activity origin */
<<set $location = $activityOrigin>>
<<set _momName = "Mom">>
<<set _momFriendLevel = $characters.mother.stats.friendshipLevel || 1>>
<<set _momFriendship = $characters.mother.stats.friendship || 0>>
/* Check requirements for yoga */
<<checkOutfitStyle "sporty" 1 0>> /* Requires: sporty outfit (1+ items) */
<<checkInventoryItem "yoga_mat">> /* Requires: yoga mat */
/* Combine check results */
<<set _canDoYoga = (State.temporary.outfitCheckResult.allowed && State.temporary.inventoryCheckResult.allowed)>>
<<set _errorReason = "">>
<<if !State.temporary.outfitCheckResult.allowed>>
<<set _errorReason = State.temporary.outfitCheckResult.reason>>
<</if>>
<<if !State.temporary.inventoryCheckResult.allowed>>
<<if _errorReason !== "">>
<<set _errorReason += " " + State.temporary.inventoryCheckResult.reason>>
<<else>>
<<set _errorReason = State.temporary.inventoryCheckResult.reason>>
<</if>>
<</if>>
<</silently>>
/* If checks fail, show error and return */
<<if !_canDoYoga>>
<<narrative "Can't Do Yoga">>
<<print _errorReason>>
<</narrative>>
<div class="location-actions">
<<btn "Change Clothes" "Wardrobe">><</btn>>
<<btn "Back" $location>><</btn>>
</div>
<<else>>
/* Outfit check passed, show yoga options */
<<narrative "Living Room">>
You clear some space in the living room and roll out your yoga mat. The soft carpet underneath makes for a comfortable surface.
As you stretch your arms, you look over at Mom, who is relaxing nearby. She notices your preparations and watches with interest.
<</narrative>>
<<image "assets/content/scenes/maplewood/FamilyHouse/livingroom/yogaSetup.webp">>
<div class="location-actions">
<<set _canInviteMomYoga = (_momFriendLevel >= 2) || (_momFriendLevel === 1 && _momFriendship > 30)>>
<<if _canInviteMomYoga>>
<<btn "Invite Mom to join you" "runYogaMom" "default" 25>><</btn>>
<<else>>
<<btn "Invite Mom (She looks busy)" "" "locked" "Need closer friendship with Mom (level 1, over 30 progress)">><</btn>>
<</if>>
<<btn "Do Yoga Alone" "runYogaSolo" "default" 25>><</btn>>
</div>
<div class="location-actions">
<<btn "Back" $location>><</btn>>
</div>
<</if>>
</div><<silently>>
<<logDailyActivity "mother" "yoga">>
<<set _yogaActRet = $activityOrigin || "fhLivingroom">>
<<requireMinEnergy 25 _yogaActRet "Not enough energy.">>
/* Set location from activity origin */
<<set $location = $activityOrigin>>
<<set $daily.yogaDone = true>>
<<advanceTime 30 "relax">>
<<loseStat "energy" 15>>
<<loseStat "stress" 15>>
<<flushNotifications>>
<<gainSkill "physical" "yoga" 1 20>>
<<gainStat "friendship" 2 "mother">>
<<recalculateStats>>
/* Text variations */
<<set _variation = random(1, 2)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "\"Mind if I join you?\" Mom asks with a smile when you invite her. You spend 30 minutes going through a routine together. She's surprisingly flexible, though she laughs when she loses balance during tree pose. \"That was lovely,\" she says afterwards. \"We should do this more often.\"">>
<<case 2>>
<<set _sceneText = "Mom lights up at your invitation. \"I used to do yoga every morning before you kids came along,\" she admits. She guides you through some poses you've never tried, gently correcting your form. It's nice learning something new from her.">>
<</switch>>
<</silently>>
<<narrative "Yoga with Mom">>
_sceneText
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/livingroom/yoga/mom/yogaMom1.mp4" "100%">>
<div class="location-actions">
<<btn "Finish" $location>><</btn>>
</div><<silently>>
<<set _yogaActRet = $activityOrigin || "fhLivingroom">>
<<requireMinEnergy 25 _yogaActRet "Not enough energy.">>
/* Set location from activity origin */
<<set $location = $activityOrigin>>
<<set $daily.yogaDone = true>>
<<advanceTime 30 "relax">>
<<loseStat "energy" 15>>
<<loseStat "stress" 15>>
<<flushNotifications>>
<<gainSkill "physical" "yoga" 1 20>>
<<recalculateStats>>
/* Location-based content */
<<if $location === "fhBackyard">>
/* BACKYARD YOGA */
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/backyard/yogaBackyard.webp">>
<<set _variation = random(1, 3)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "You roll out your mat on the soft grass and begin your routine. The fresh air fills your lungs as you move through each pose. Birds chirp in the trees above, providing a natural soundtrack to your practice.">>
<<case 2>>
<<set _sceneText = "The morning sun warms your skin as you stretch in the backyard. There's something grounding about practicing yoga outside - feeling the earth beneath you, the breeze on your face. You hold each pose a little longer, savoring the moment.">>
<<case 3>>
<<set _sceneText = "You find a shady spot under the tree and lay out your mat. The backyard is peaceful at this hour. You flow through your poses slowly, feeling more connected to your body with each breath of fresh air.">>
<</switch>>
/* Random yoga video (backyard): yogaBackyard1.mp4 – yogaBackyard9.mp4 */
<<set _yogaVidNum = random(1, 9)>>
<<set _yogaVid = "assets/content/scenes/maplewood/FamilyHouse/backyard/yoga/yogaBackyard" + _yogaVidNum + ".mp4">>
<<else>>
/* LIVING ROOM YOGA */
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/yogaSetup.webp">>
<<set _variation = random(1, 3)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "You spend the next 30 minutes moving through various poses. Downward dog, warrior, tree pose... your breathing steadies and your muscles stretch pleasantly. It's a good workout, leaving you feeling centered and refreshed.">>
<<case 2>>
<<set _sceneText = "Today you decide to challenge yourself with some harder poses. A few wobbles and one near-fall later, you're sweating but satisfied. Progress is progress, even when it's messy.">>
<<case 3>>
<<set _sceneText = "You focus on gentle stretches today, holding each pose a little longer than usual. Your mind wanders peacefully as your body loosens up. Sometimes the slow sessions are exactly what you need.">>
<</switch>>
/* Random yoga video (living room): yogaLivingroom1.mp4 – yogaLivingroom13.mp4 */
<<set _yogaVidNum = random(1, 13)>>
<<set _yogaVid = "assets/content/scenes/maplewood/FamilyHouse/livingroom/yoga/solo/yogaLivingroom" + _yogaVidNum + ".mp4">>
<</if>>
<</silently>>
<<narrative "Yoga Session">>
_sceneText
<</narrative>>
<<if _yogaVid>>
<<vid _yogaVid "100%">>
<<else>>
<<image _sceneImage "80%">>
<</if>>
<div class="location-actions">
<<btn "Finish" $location>><</btn>>
</div><<nobr>><<set $location = "salonBeautyFifth">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Headlines Salon">>The most prestigious salon in town. Celebrity stylists and exclusive treatments.<</narrative>>
<<showLocationChars "salonBeautyFifth">>
<<navMenu>><<navCard "streetFifth">><</navMenu>><<nobr>><<set $location = "salonBeautyMall">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Headlines Salon">>A full-service salon offering hair, nails, and spa treatments. Stylists work their magic on clients.<</narrative>>
<<showLocationChars "salonBeautyMall">>
<<navMenu>><<navCard "floorThird">><</navMenu>><<nobr>>
<<set $location = "fhBedroom">>
<<narrative "Alarm Settings">>
Set your wake-up preferences. You can configure different schedules for weekdays and weekends.
<</narrative>>
<<image "assets/content/scenes/maplewood/familyHouse/bedroom/alarm.webp">>
<<silently>>
/* Initialize alarm object if missing (safety for legacy saves/hot-reload) */
<<if $alarm is undefined>>
<<set $alarm = {
weekdayEnabled: false,
weekdayHour: 7,
weekdayMinute: 0,
weekendEnabled: false,
weekendHour: 10,
weekendMinute: 0
}>>
<</if>>
/* Create temporary alarm settings (copy from $alarm) */
<<set _tempAlarm = {
weekdayEnabled: $alarm.weekdayEnabled,
weekdayHour: $alarm.weekdayHour,
weekdayMinute: $alarm.weekdayMinute,
weekendEnabled: $alarm.weekendEnabled,
weekendHour: $alarm.weekendHour,
weekendMinute: $alarm.weekendMinute
}>>
/* Prepare Dropdown Data */
<<set _hours = []>>
<<for _i to 0; _i < 24; _i++>>
<<set _label = _i.toString().padStart(2, '0')>>
<<run _hours.push({label: _label, value: _i})>>
<</for>>
<<set _minutes = [
{label: "00", value: 0},
{label: "15", value: 15},
{label: "30", value: 30},
{label: "45", value: 45}
]>>
<</silently>>
<div class="alarm-controls">
<div class="alarm-grid">
<div class="alarm-card" id="alarm-card-weekday">
<div class="alarm-header">
<h4 class="alarm-title">Weekday</h4>
<<uiToggle "_tempAlarm.weekdayEnabled">>
</div>
<div class="alarm-time-box" id="time-box-weekday">
<div class="alarm-time-inputs">
<div class="alarm-input-group">
<span class="alarm-label">Hour</span>
<<uiDropdown "_tempAlarm.weekdayHour" _hours>>
</div>
<span class="alarm-separator">:</span>
<div class="alarm-input-group">
<span class="alarm-label">Minute</span>
<<uiDropdown "_tempAlarm.weekdayMinute" _minutes>>
</div>
</div>
</div>
</div>
<!-- Weekend Settings -->
<div class="alarm-card" id="alarm-card-weekend">
<!-- Header with Toggle -->
<div class="alarm-header">
<h4 class="alarm-title">Weekend</h4>
<<uiToggle "_tempAlarm.weekendEnabled">>
</div>
<!-- Time Selection Area -->
<div class="alarm-time-box" id="time-box-weekend">
<div class="alarm-time-inputs">
<div class="alarm-input-group">
<span class="alarm-label">Hour</span>
<<uiDropdown "_tempAlarm.weekendHour" _hours>>
</div>
<span class="alarm-separator">:</span>
<div class="alarm-input-group">
<span class="alarm-label">Minute</span>
<<uiDropdown "_tempAlarm.weekendMinute" _minutes>>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="location-actions" style="margin-top: 30px; display: flex; justify-content: center; gap: 20px;">
<<btn "Set Alarm" "fhBed">>
<<set $alarm.weekdayEnabled = _tempAlarm.weekdayEnabled>>
<<set $alarm.weekdayHour = _tempAlarm.weekdayHour>>
<<set $alarm.weekdayMinute = _tempAlarm.weekdayMinute>>
<<set $alarm.weekendEnabled = _tempAlarm.weekendEnabled>>
<<set $alarm.weekendHour = _tempAlarm.weekendHour>>
<<set $alarm.weekendMinute = _tempAlarm.weekendMinute>>
<<run window.notifySuccess("Alarm updated successfully!")>>
<</btn>>
<<btn "Bed" "fhBed">><</btn>>
</div>
<</nobr>>
<<script>>
// Helper to sync UI state
function updateVisuals() {
// We use finding by ID which is robust
const $weekdayToggle = $('#alarm-card-weekday').find('input[type="checkbox"]');
const $weekendToggle = $('#alarm-card-weekend').find('input[type="checkbox"]');
const $weekdayBox = $('#time-box-weekday');
const $weekendBox = $('#time-box-weekend');
// Update Weekday
if ($weekdayToggle.prop('checked')) {
$weekdayBox.removeClass('disabled');
} else {
$weekdayBox.addClass('disabled');
}
// Update Weekend
if ($weekendToggle.prop('checked')) {
$weekendBox.removeClass('disabled');
} else {
$weekendBox.addClass('disabled');
}
}
// Run after DOM is fully ready (increased delay slightly)
setTimeout(updateVisuals, 100);
// Document-level listener to catch all toggle changes
$(document).on('change.alarm', '.ui-toggle-input', function() {
// Small delay to let the checkbox state settle
setTimeout(updateVisuals, 20);
});
// Clean up listener when leaving passage prevents potential double-firing
$(document).one(':passageinit', function() {
$(document).off('change.alarm');
});
<</script>><<nobr>>
<<set $hideTopbarNav = true>>
<<set $hideTopbarTimebox = true>>
<<set $hideTopbarNotifications = true>>
<<set $hideRightbar = true>>
<<set $hideTopbar = true>>
<<set $hideTopbarHamburger = false>>
<<run $('body').addClass('fullscreen-centered')>>
<div class="page-wrapper">
<div class="game-setup-container">
<h1 class="page-title">Game Setup</h1>
<div class="setup-header-line"></div>
<p class="page-subtitle">
Configure your character and world. These choices define your starting experience.
</p>
<!-- 1. CHARACTER CREATION -->
<div class="settings-section setup-accordion-item">
<div class="settings-section-header setup-accordion-header">
<div style="display: flex; align-items: center; gap: 0.5rem;"><span class="icon icon-user"></span> Character Identity</div>
<span class="setup-accordion-toggle icon icon-chevron-down"></span>
</div>
<div class="settings-section-body setup-accordion-content">
<div class="player-name-card">
<div class="family-member-header">You</div>
<div class="form-grid-2">
<div class="form-input-group">
<label>First name</label>
<input type="text" id="characterFirstName" @value="$player.firstName" maxlength="40" autocomplete="given-name">
</div>
<div class="form-input-group">
<label>Surname</label>
<input type="text" id="characterLastName" @value="$player.lastName" maxlength="40" autocomplete="family-name">
</div>
</div>
</div>
<div class="note-box warning">
The surname applies to you, your mother, father, and sibling for the whole game.
</div>
<div class="setup-subsection-title" style="margin-top: 1.25rem; margin-bottom: 0.5rem; font-weight: 600;">Household</div>
<div class="family-member-row">
<div class="family-member-card">
<div class="family-member-header">Mother</div>
<div class="form-input-group">
<label>First name</label>
<input type="text" id="familyMotherFirstName" @value="$characters.mother.firstName" maxlength="40" autocomplete="off">
</div>
<div class="form-input-group">
<label>Called as</label>
<input type="text" id="familyMotherStatus" @value="$characters.mother.status" maxlength="24" placeholder="Mom, Mother, Step-Mother..." autocomplete="off">
</div>
</div>
<div class="family-member-card">
<div class="family-member-header">Father</div>
<div class="form-input-group">
<label>First name</label>
<input type="text" id="familyFatherFirstName" @value="$characters.father.firstName" maxlength="40" autocomplete="off">
</div>
<div class="form-input-group">
<label>Called as</label>
<input type="text" id="familyFatherStatus" @value="$characters.father.status" maxlength="24" placeholder="Dad, Father, Step-Father..." autocomplete="off">
</div>
</div>
<div class="family-member-card">
<div class="family-member-header">Sibling</div>
<div class="form-input-group">
<label>First name</label>
<input type="text" id="familyBrotherFirstName" @value="$characters.brother.firstName" maxlength="40" autocomplete="off">
</div>
<div class="form-input-group">
<label>Called as</label>
<input type="text" id="familyBrotherStatus" @value="$characters.brother.status" maxlength="24" placeholder="Brother, Step-Brother..." autocomplete="off">
</div>
</div>
</div>
<div class="note-box warning" >
“Called as” is how they appear in dialogue and on the character profile (e.g. Sarah (Mother)) (WIP).
</div>
</div>
</div>
<!-- 2. BODY CUSTOMIZATION -->
<div class="settings-section setup-accordion-item">
<div class="settings-section-header setup-accordion-header">
<div style="display: flex; align-items: center; gap: 0.5rem;"><span class="icon icon-body"></span> Body Customization</div>
<span class="setup-accordion-toggle icon icon-chevron-down"></span>
</div>
<div class="settings-section-body setup-accordion-content">
<!-- Hidden Inputs for JS to update -->
<input type="hidden" id="valEyeColor" value="Black">
<input type="hidden" id="valHairColor" value="Black">
<input type="hidden" id="valHairLength" value="Short">
<input type="hidden" id="valHairStyle" value="Straight">
<input type="hidden" id="valBustSize" value="A">
<input type="hidden" id="valHipSize" value="Slim">
<input type="hidden" id="valHeight" value="170">
<!-- Height -->
<div class="visual-label">Height</div>
<div class="height-control-wrapper">
<div class="height-value-display"><span id="heightValue">170</span> cm</div>
<div class="custom-slider-container" data-slider="height">
<div class="custom-slider-track">
<div class="custom-slider-fill" style="width: 50%;"></div>
<div class="custom-slider-thumb" style="left: 50%;">
<div class="custom-slider-tooltip">170 cm</div>
</div>
</div>
</div>
</div>
<!-- Eye Color -->
<div class="visual-label">Eye Color</div>
<div class="color-grid" id="gridEyeColor">
<div class="color-option selected" data-value="Black"><div class="color-swatch" style="background: #1a1a1a;"></div><div class="color-name">Black</div></div>
<div class="color-option" data-value="Brown"><div class="color-swatch" style="background: #5D4037;"></div><div class="color-name">Brown</div></div>
<div class="color-option" data-value="Hazel"><div class="color-swatch" style="background: #8B6914;"></div><div class="color-name">Hazel</div></div>
<div class="color-option" data-value="Green"><div class="color-swatch" style="background: #4A7C59;"></div><div class="color-name">Green</div></div>
<div class="color-option" data-value="Blue"><div class="color-swatch" style="background: #5B8FA8;"></div><div class="color-name">Blue</div></div>
<div class="color-option" data-value="Gray"><div class="color-swatch" style="background: #7A8B8B;"></div><div class="color-name">Gray</div></div>
</div>
<!-- Hair Color -->
<div class="visual-label">Hair Color</div>
<div class="color-grid" id="gridHairColor">
<div class="color-option selected" data-value="Black"><div class="color-swatch" style="background: #1a1a1a;"></div><div class="color-name">Black</div></div>
<div class="color-option" data-value="Dark Brown"><div class="color-swatch" style="background: #2C1810;"></div><div class="color-name">D.Brown</div></div>
<div class="color-option" data-value="Brown"><div class="color-swatch" style="background: #6B4423;"></div><div class="color-name">Brown</div></div>
<div class="color-option" data-value="Auburn"><div class="color-swatch" style="background: #A0522D;"></div><div class="color-name">Auburn</div></div>
<div class="color-option" data-value="Light Brown"><div class="color-swatch" style="background: #CD853F;"></div><div class="color-name">L.Brown</div></div>
<div class="color-option" data-value="Blonde"><div class="color-swatch" style="background: #DAA520;"></div><div class="color-name">Blonde</div></div>
<div class="color-option" data-value="Red"><div class="color-swatch" style="background: #8B0000;"></div><div class="color-name">Red</div></div>
<div class="color-option" data-value="Gray"><div class="color-swatch" style="background: #C0C0C0;"></div><div class="color-name">Gray</div></div>
</div>
<!-- Hair Length -->
<div class="visual-label">Hair Length</div>
<div class="visual-grid" id="gridHairLength">
<div class="visual-option selected" data-value="Short"><div class="visual-img"><img @src="setup.playerAppearanceImages.hairShort" alt="Short Hair"></div><div class="visual-name">Short</div></div>
<div class="visual-option" data-value="Medium"><div class="visual-img"><img @src="setup.playerAppearanceImages.hairMedium" alt="Medium Hair"></div><div class="visual-name">Medium</div></div>
<div class="visual-option" data-value="Long"><div class="visual-img"><img @src="setup.playerAppearanceImages.hairLong" alt="Long Hair"></div><div class="visual-name">Long</div></div>
<div class="visual-option" data-value="Very Long"><div class="visual-img"><img @src="setup.playerAppearanceImages.hairVeryLong" alt="Very Long Hair"></div><div class="visual-name">Very Long</div></div>
</div>
<!-- Hair Style (Dynamic - appears after length selection) -->
<div class="hair-style-section" id="hairStyleSection">
<div class="visual-label">Hair Style</div>
<div class="visual-grid" id="gridHairStyle">
<div class="visual-option selected" data-value="Straight" data-length="Short"><div class="visual-img"><img id="imgStraight" @src="setup.playerAppearanceImages.hairShortStraight" alt="Straight Hair"></div><div class="visual-name">Straight</div></div>
<div class="visual-option" data-value="Wavy" data-length="Short"><div class="visual-img"><img id="imgWavy" @src="setup.playerAppearanceImages.hairShortWavy" alt="Wavy Hair"></div><div class="visual-name">Wavy</div></div>
<div class="visual-option" data-value="Curly" data-length="Short"><div class="visual-img"><img id="imgCurly" @src="setup.playerAppearanceImages.hairShortCurly" alt="Curly Hair"></div><div class="visual-name">Curly</div></div>
</div>
</div>
<!-- Bust Size (A/B only – low starting looks) -->
<div class="visual-label">Bust Size</div>
<div class="visual-grid" id="gridBustSize">
<div class="visual-option selected" data-value="A"><div class="visual-img"><img @src="setup.playerAppearanceImages.bustA" alt="A Cup"></div><div class="visual-name">A-Cup</div></div>
<div class="visual-option" data-value="B"><div class="visual-img"><img @src="setup.playerAppearanceImages.bustB" alt="B Cup"></div><div class="visual-name">B-Cup</div></div>
</div>
<!-- Hip Size (Slim/Medium only – low starting looks; waist auto) -->
<div class="visual-label">Hip Size</div>
<div class="visual-grid" id="gridHipSize">
<div class="visual-option selected" data-value="Slim"><div class="visual-img"><img @src="setup.playerAppearanceImages.hipSlim" alt="Slim Hips"></div><div class="visual-name">Slim</div></div>
<div class="visual-option" data-value="Medium"><div class="visual-img"><img @src="setup.playerAppearanceImages.hipMedium" alt="Medium Hips"></div><div class="visual-name">Medium</div></div>
</div>
</div>
</div>
<!-- 3. SIMULATION SETTINGS -->
<div class="settings-section setup-accordion-item">
<div class="settings-section-header setup-accordion-header">
<div style="display: flex; align-items: center; gap: 0.5rem;"><span class="icon icon-settings"></span> Simulation Settings</div>
<span class="setup-accordion-toggle icon icon-chevron-down"></span>
</div>
<div class="settings-section-body setup-accordion-content">
<div class="note-box warning">
⚠️ These settings cannot be changed after starting the game.
</div>
<div class="note-box warning">
⚠️ Closed settings were not fully tested during the beta process.
</div>
<div class="flex-center" style="justify-content: flex-end; margin-bottom: 0.75rem;">
<a href="#" class="link-internal btn-style btn-default settings-bulk-btn settings-bulk-btn-simulation" data-bulk-state="on">Enable All</a>
</div>
<div class="sim-grid">
<div class="sim-group-title">Basic Needs</div>
<div class="sim-item">
<div>
<div class="sim-label">Hunger</div>
<div class="sim-desc">Need to eat regularly</div>
</div>
<button class="setting-toggle-btn active" data-setting="trackHunger">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Thirst</div>
<div class="sim-desc">Need to drink water</div>
</div>
<button class="setting-toggle-btn active" data-setting="trackThirst">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Bladder</div>
<div class="sim-desc">Need to use bathroom</div>
</div>
<button class="setting-toggle-btn active" data-setting="trackBladder">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Calories</div>
<div class="sim-desc">Track intake vs burn</div>
</div>
<button class="setting-toggle-btn active" data-setting="trackCalories">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Hygiene Impact</div>
<div class="sim-desc">Bad smell repels people</div>
</div>
<button class="setting-toggle-btn active" data-setting="hygieneRequirement">ON</button>
</div>
<div class="sim-group-title">Appearance & Decay</div>
<div class="sim-item">
<div>
<div class="sim-label">Hair Growth</div>
<div class="sim-desc">Hair gets longer daily</div>
</div>
<button class="setting-toggle-btn active" data-setting="hairGrowth">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Hair Messiness</div>
<div class="sim-desc">Wake up with messy hair</div>
</div>
<button class="setting-toggle-btn active" data-setting="hairMessiness">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Body Hair</div>
<div class="sim-desc">Body hair grows over time</div>
</div>
<button class="setting-toggle-btn active" data-setting="bodyHairGrowth">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Makeup Fade</div>
<div class="sim-desc">Makeup wears off daily</div>
</div>
<button class="setting-toggle-btn active" data-setting="makeupWearOff">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Body Changes</div>
<div class="sim-desc">Muscle/Fat gain/loss</div>
</div>
<button class="setting-toggle-btn active" data-setting="bodyDegradation">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Hair Care Decay</div>
<div class="sim-desc">Hair care drops without routine</div>
</div>
<button class="setting-toggle-btn active" data-setting="hairCareDecay">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Face Care Decay</div>
<div class="sim-desc">Skincare drops without routine</div>
</div>
<button class="setting-toggle-btn active" data-setting="faceCareDecay">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Dental Care Decay</div>
<div class="sim-desc">Teeth care drops without brushing</div>
</div>
<button class="setting-toggle-btn active" data-setting="dentalCareDecay">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Skill Decay</div>
<div class="sim-desc">Unused skills degrade</div>
</div>
<button class="setting-toggle-btn active" data-setting="skillDecay">ON</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Relation Decay</div>
<div class="sim-desc">Unseen friends drift apart</div>
</div>
<button class="setting-toggle-btn active" data-setting="relationshipDecay">ON</button>
</div>
</div>
</div>
</div>
<!-- 4. CONTENT PREFERENCES -->
<div class="settings-section setup-accordion-item">
<div class="settings-section-header setup-accordion-header">
<div style="display: flex; align-items: center; gap: 0.5rem;"><span class="icon icon-eye"></span> Content Preferences</div>
<span class="setup-accordion-toggle icon icon-chevron-down"></span>
</div>
<div class="settings-section-body setup-accordion-content">
<div class="note-box warning">
⚠️ Select content you are comfortable with. Can be changed anytime.
</div>
<div class="note-box warning">
⚠️ Recommend having all settings enabled for the best experience.
</div>
<div class="flex-center" style="justify-content: flex-end; margin-bottom: 0.75rem;">
<a href="#" class="link-internal btn-style btn-default settings-bulk-btn settings-bulk-btn-content" data-bulk-state="on">Enable All</a>
</div>
<div class="sim-grid">
<div class="sim-item">
<div>
<div class="sim-label">Male Sexual</div>
<div class="sim-desc">Scenes with men</div>
</div>
<button class="setting-toggle-btn" data-setting="maleSexual" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Female Sexual</div>
<div class="sim-desc">Scenes with women</div>
</div>
<button class="setting-toggle-btn" data-setting="femaleSexual" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Trans/Futa</div>
<div class="sim-desc">Penis-having women</div>
</div>
<button class="setting-toggle-btn" data-setting="futaTrans" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Pregnancy</div>
<div class="sim-desc">Risk of getting pregnant</div>
</div>
<button class="setting-toggle-btn" data-setting="pregnancy" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Incest</div>
<div class="sim-desc">Family relations</div>
</div>
<button class="setting-toggle-btn" data-setting="incest" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">NTR / Cheating</div>
<div class="sim-desc">Betrayal content</div>
</div>
<button class="setting-toggle-btn" data-setting="ntr" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">BDSM</div>
<div class="sim-desc">Bondage and dominance</div>
</div>
<button class="setting-toggle-btn" data-setting="bdsm" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Non-Consensual</div>
<div class="sim-desc">Forced interactions</div>
</div>
<button class="setting-toggle-btn" data-setting="nonConsensual" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Public / Exhibition</div>
<div class="sim-desc">Being seen in public</div>
</div>
<button class="setting-toggle-btn" data-setting="publicExhibition" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Lactation</div>
<div class="sim-desc">Breast milk production</div>
</div>
<button class="setting-toggle-btn" data-setting="lactation" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Foot Fetish</div>
<div class="sim-desc">Focus on feet</div>
</div>
<button class="setting-toggle-btn" data-setting="feet" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Watersports</div>
<div class="sim-desc">Urine related content</div>
</div>
<button class="setting-toggle-btn" data-setting="watersports" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Scat</div>
<div class="sim-desc">Feces related content</div>
</div>
<button class="setting-toggle-btn" data-setting="scat" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Gore / Violence</div>
<div class="sim-desc">Blood and injury</div>
</div>
<button class="setting-toggle-btn" data-setting="goreViolence" data-category="content">OFF</button>
</div>
<div class="sim-item">
<div>
<div class="sim-label">Ageplay</div>
<div class="sim-desc">Age difference dynamics</div>
</div>
<button class="setting-toggle-btn" data-setting="ageplay" data-category="content">OFF</button>
</div>
</div>
</div>
</div>
<!-- START GAME BUTTON -->
<div class="flex-center mt-4 mb-4" id="start-game-container">
<<btn "Continue" "" "accent">><</btn>>
</div>
</div>
</div>
<</nobr>>
<<script>>
$(document).on(':passagedisplay', function(ev) {
if (ev.passage.title !== 'settingsPage') return;
const State = SugarCube.State;
const vars = State.variables;
const $ = jQuery;
// --- INITIALIZATION ---
// 1. Restore Toggles (Using .active instead of .on/.off to match accordion.js)
function restoreToggle(setting, obj) {
if (!obj || obj[setting] === undefined) return;
const val = obj[setting];
const $btn = $('.setting-toggle-btn[data-setting="' + setting + '"]');
if (val) {
$btn.addClass('active').text('ON');
} else {
$btn.removeClass('active').text('OFF');
}
}
// Restore Game Settings
if (vars.gameSettings) {
Object.keys(vars.gameSettings).forEach(key => restoreToggle(key, vars.gameSettings));
}
// Restore Content Preferences
if (vars.contentPreferences) {
Object.keys(vars.contentPreferences).forEach(key => restoreToggle(key, vars.contentPreferences));
}
// 2. Restore Visual Selections
function restoreSelection(group, value) {
if (!value) return;
const $container = $('#grid' + group);
$container.find('.selected').removeClass('selected');
$container.find('[data-value="' + value + '"]').addClass('selected');
$('#val' + group).val(value);
}
if (vars.player) {
restoreSelection('EyeColor', vars.player.eyeColor);
restoreSelection('HairColor', vars.player.hairColor);
restoreSelection('HairLength', vars.player.hairLength);
restoreSelection('HairStyle', vars.player.hairStyle || 'Straight');
restoreSelection('BustSize', vars.player.bustSize);
restoreSelection('HipSize', vars.player.hipSize);
if (vars.player.firstName) $('#characterFirstName').val(vars.player.firstName);
if (vars.player.lastName) $('#characterLastName').val(vars.player.lastName);
if (vars.characters) {
const m = vars.characters.mother;
const f = vars.characters.father;
const b = vars.characters.brother;
if (m) {
if (m.firstName) $('#familyMotherFirstName').val(m.firstName);
if (m.status) $('#familyMotherStatus').val(m.status);
}
if (f) {
if (f.firstName) $('#familyFatherFirstName').val(f.firstName);
if (f.status) $('#familyFatherStatus').val(f.status);
}
if (b) {
if (b.firstName) $('#familyBrotherFirstName').val(b.firstName);
if (b.status) $('#familyBrotherStatus').val(b.status);
}
}
// Restore height
if (vars.body && vars.body.height) {
const height = vars.body.height;
const percentage = ((height - 150) / 40) * 100;
const container = $('.custom-slider-container[data-slider="height"]');
container.find('.custom-slider-fill').css('width', percentage + '%');
container.find('.custom-slider-thumb').css('left', percentage + '%');
container.find('.custom-slider-tooltip').text(height + ' cm');
$('#heightValue').text(height);
$('#valHeight').val(height);
}
// Update hair style images based on current length
if (vars.player.hairLength) {
updateHairStyleImages(vars.player.hairLength);
}
}
// Height Custom Slider Events
(function() {
const container = $('.custom-slider-container[data-slider="height"]');
const track = container.find('.custom-slider-track');
const fill = container.find('.custom-slider-fill');
const thumb = container.find('.custom-slider-thumb');
const tooltip = container.find('.custom-slider-tooltip');
let isDragging = false;
const updateSlider = (clientX) => {
const trackRect = track[0].getBoundingClientRect();
const trackWidth = trackRect.width;
let position = clientX - trackRect.left;
position = Math.max(0, Math.min(position, trackWidth));
const percentage = (position / trackWidth) * 100;
// Height range: 150-190 cm
const height = Math.round(150 + (percentage / 100) * 40);
fill.css('width', percentage + '%');
thumb.css('left', percentage + '%');
tooltip.text(height + ' cm');
$('#heightValue').text(height);
$('#valHeight').val(height);
};
track.on('mousedown', function(e) {
if (e.target === thumb[0] || thumb.has(e.target).length) return;
updateSlider(e.clientX);
isDragging = true;
thumb.addClass('dragging');
});
thumb.on('mousedown', function(e) {
e.preventDefault();
isDragging = true;
thumb.addClass('dragging');
});
$(document).on('mousemove.height-slider', function(e) {
if (!isDragging) return;
updateSlider(e.clientX);
});
$(document).on('mouseup.height-slider', function() {
if (isDragging) {
isDragging = false;
thumb.removeClass('dragging');
}
});
})();
// Helper: Update hair style images based on selected length
function updateHairStyleImages(length) {
const lengthKey = length.replace(' ', ''); // "Very Long" -> "VeryLong"
const images = SugarCube.setup.playerAppearanceImages;
if (images) {
$('#imgStraight').attr('src', images['hair' + lengthKey + 'Straight'] || '');
$('#imgWavy').attr('src', images['hair' + lengthKey + 'Wavy'] || '');
$('#imgCurly').attr('src', images['hair' + lengthKey + 'Curly'] || '');
}
// Update data-length on style options
$('#gridHairStyle .visual-option').attr('data-length', length);
}
// --- EVENT LISTENERS ---
// 1. Color & Visual Options Click (except hair length which has special handling)
$('.color-option, .visual-option').not('#gridHairLength .visual-option').on('click', function() {
const $this = $(this);
const $container = $this.parent();
const value = $this.data('value');
$container.find('.selected').removeClass('selected');
$this.addClass('selected');
const gridId = $container.attr('id');
const inputId = gridId.replace('grid', 'val');
$('#' + inputId).val(value);
});
// 1b. Hair Length Click - Special handling to update Hair Style images
$('#gridHairLength .visual-option').on('click', function() {
const $this = $(this);
const $container = $this.parent();
const length = $this.data('value');
$container.find('.selected').removeClass('selected');
$this.addClass('selected');
$('#valHairLength').val(length);
// Update Hair Style images dynamically
updateHairStyleImages(length);
});
// 2. Toggle Buttons Click -> HANDLED BY accordion.js
// 2b. Bulk Enable / Disable All (delegated <<btn>> macro uses one-shot clicks; use plain <a>)
function setBulkToggleState($buttons, isEnabled) {
if (!$buttons || !$buttons.length) return;
$buttons.toggleClass('active', isEnabled);
$buttons.text(isEnabled ? 'ON' : 'OFF');
}
function getBulkTargetButtons(target) {
if (target === 'content') {
return $('.setting-toggle-btn[data-category="content"]');
}
return $('.setting-toggle-btn').not('[data-category="content"]');
}
function updateBulkButtonLabel(target) {
const $targetButtons = getBulkTargetButtons(target);
const allEnabled = $targetButtons.length > 0 && $targetButtons.filter('.active').length === $targetButtons.length;
const $bulkBtn = target === 'content'
? $('.settings-bulk-btn-content')
: $('.settings-bulk-btn-simulation');
if (!$bulkBtn.length) return;
const nextState = allEnabled ? 'off' : 'on';
$bulkBtn.text(allEnabled ? 'Disable All' : 'Enable All');
$bulkBtn.attr('data-bulk-state', nextState);
}
updateBulkButtonLabel('simulation');
updateBulkButtonLabel('content');
$(document).off('click.settingsBulk', '.settings-bulk-btn');
$(document).on('click.settingsBulk', '.settings-bulk-btn', function(e) {
e.preventDefault();
e.stopPropagation();
const target = $(this).hasClass('settings-bulk-btn-content') ? 'content' : 'simulation';
const isEnabled = $(this).attr('data-bulk-state') === 'on';
const $targetButtons = getBulkTargetButtons(target);
setBulkToggleState($targetButtons, isEnabled);
updateBulkButtonLabel(target);
});
$(document).off('click.settingsBulkLabel', '.setting-toggle-btn');
$(document).on('click.settingsBulkLabel', '.setting-toggle-btn', function() {
const isContent = $(this).data('category') === 'content';
updateBulkButtonLabel(isContent ? 'content' : 'simulation');
});
// 3. Start Game Click (Updated to target widget output)
$('#start-game-container .btn-style').on('click', function(e) {
e.preventDefault();
// Ensure player object exists
if (!vars.player) {
vars.player = {};
}
// Save Character Info
vars.player.firstName = $('#characterFirstName').val() || "Alex";
vars.player.lastName = $('#characterLastName').val() || "Taylor";
// Update Family Last Names to match Player
if (vars.characters) {
const lastName = vars.player.lastName;
if (vars.characters.father) {
vars.characters.father.lastName = lastName;
}
if (vars.characters.mother) {
vars.characters.mother.lastName = lastName;
}
if (vars.characters.brother) {
vars.characters.brother.lastName = lastName;
}
const m = vars.characters.mother;
const f = vars.characters.father;
const b = vars.characters.brother;
if (m) {
const mfn = ($('#familyMotherFirstName').val() || '').trim() || 'Sarah';
m.firstName = mfn;
m.name = mfn;
m.status = ($('#familyMotherStatus').val() || '').trim() || 'Mother';
}
if (f) {
f.firstName = ($('#familyFatherFirstName').val() || '').trim() || 'Robert';
f.status = ($('#familyFatherStatus').val() || '').trim() || 'Father';
}
if (b) {
b.firstName = ($('#familyBrotherFirstName').val() || '').trim() || 'Jake';
b.status = ($('#familyBrotherStatus').val() || '').trim() || 'Brother';
}
}
// Save Body Customization
vars.player.eyeColor = $('#valEyeColor').val();
vars.player.hairColor = $('#valHairColor').val();
vars.player.hairLength = $('#valHairLength').val();
vars.player.hairStyle = $('#valHairStyle').val();
vars.player.bustSize = $('#valBustSize').val();
vars.player.hipSize = $('#valHipSize').val();
// Save Height to $body
const height = parseInt($('#valHeight').val()) || 170;
vars.body.height = height;
// Auto-calculate weight for Normal body type (BMI ~21.5)
const heightM = height / 100;
vars.body.weight = Math.round(21.5 * heightM * heightM);
// Bust: A/B only (low starting looks)
const bustSizes = { 'A': 80, 'B': 85 };
vars.body.bust = bustSizes[vars.player.bustSize] ?? 80;
// Hip: Slim/Medium only (low starting looks)
const hipSizes = { 'Slim': 85, 'Medium': 95 };
vars.body.hips = hipSizes[vars.player.hipSize] ?? 85;
// Waist: auto (ratio ~0.77 so Slim 85 → waist ~65–66)
vars.body.waist = Math.round(vars.body.hips * 0.77);
// Only A/B + Slim/Medium at start, so body type is Normal
vars.body.bodyType = "Normal";
vars.body.bmi = Math.round((vars.body.weight / (heightM * heightM)) * 10) / 10;
// Save simulation + content toggles BEFORE recalculateStats so disabled tracks
// (hunger/thirst/bladder/hygiene) clamp correctly on first run.
$('.setting-toggle-btn[data-category!="content"]').each(function() {
const key = $(this).data('setting');
vars.gameSettings[key] = $(this).hasClass('active');
});
$('.setting-toggle-btn[data-category="content"]').each(function() {
const key = $(this).data('setting');
vars.contentPreferences[key] = $(this).hasClass('active');
});
if (window.Wikifier) window.Wikifier.wikifyEval("<<recalculateStats>>");
// Navigate
SugarCube.Engine.play('confirmationPage');
});
});
<</script>><<nobr>>
<<set $location = "shopBarber">>
<<updateCharacterLocations>>
<<narrative "The Clipper Shop">>
An old-fashioned barber shop with a spinning pole. Cuts, shaves, and neighborhood gossip.
<</narrative>>
<<showLocationChars "shopBarber">>
<div class="location-actions">
<span class="link-internal btn-style btn-default locked" data-tooltip="W.I.P"><i class="icon icon-lock icon-12"></i> Shop</span>
<span class="link-internal btn-style btn-default locked" data-tooltip="W.I.P"><i class="icon icon-lock icon-12"></i> Dye Hair</span>
<span class="link-internal btn-style btn-default locked" data-tooltip="W.I.P"><i class="icon icon-lock icon-12"></i> Cut Hair</span>
</div>
<<navMenu>>
<<navCard "oldTown">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "shopBarber">>
<<set _shopItems = []>>
<<set _shopBack = "shopBarber">>
<<updateCharacterLocations>>
<</nobr>>
<<narrative "The Clipper Shop">>Hair products and grooming supplies.<</narrative>>
<<shop "The Clipper Shop" "Hair & Grooming" _shopItems _shopBack>>
<div class="location-actions">
<<btn "Back" "shopBarber">><</btn>>
</div><<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<set $location = $location || "storeCorner">>
<<logDailyActivity "shopClerkMarcus" "talk">>
<<updateCharacterLocations>>
<<set _stats = $characters.shopClerkMarcus.stats>>
<<set _level = _stats.friendshipLevel || 1>>
<<set _phase = "common">>
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.marcusTopics && setup.marcusTopics[_phase] && setup.marcusTopics[_phase][_levelKey] && setup.marcusTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.marcusTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.marcusTopics && setup.marcusTopics.common && setup.marcusTopics.common[_levelKey] && setup.marcusTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.marcusTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(State.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(State.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "Marcus is tied up with a delivery at the back. He gives you an apologetic wave through the security mirror.", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
<<if _topic.friendship>><<gainCharacterStat "shopClerkMarcus" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "shopClerkMarcus" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "shopClerkMarcus" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "shopClerkMarcus" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.shopClerkMarcus.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<nobr>>
<<set $location = "shopCoffeeOldtown">>
<<updateCharacterLocations>>
<<narrative "The Daily Grind">>A cozy coffee shop with homemade pastries. The perfect spot to relax with a book.<</narrative>>
<<showLocationChars "shopCoffeeOldtown">>
<div class="location-actions">
<<restaurantLink "dailyGrind" "Drink Here">>
<<btn "Take Away" "shopCoffeeOldtown_takeAway">><</btn>>
</div>
<<navMenu>><<navCard "oldTown">><</navMenu>>
<</nobr>><<nobr>><<set $location = "shopPawn">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Lucky's Pawn & Loan">>A cluttered pawn shop. Desperate sellers and bargain hunters alike.<</narrative>>
<<showLocationChars "shopPawn">>
<<navMenu>><<navCard "southside">><</navMenu>><<nobr>>
<<silently>>
<<set _cid = $showerEncounterTarget || (State.temporary.bathroomCheck && State.temporary.bathroomCheck.character)>>
<<unset $showerEncounterTarget>>
<<if !_cid || !$characters[_cid]>>
<<goto "fhUpperstairs">>
<</if>>
<<set _char = $characters[_cid]>>
<<set _first = _char.firstName>>
<<set _lust = (_char.stats && typeof _char.stats.lust === "number") ? _char.stats.lust : 0>>
<<set _lustLevel = (_char.stats && typeof _char.stats.lustLevel === "number") ? _char.stats.lustLevel : 1>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = (_cid === "mother" || _cid === "father") ? "fhParentsRoom" : "fhUpperstairs">>
<</if>>
<<if _cid === "mother" || _cid === "father">>
<<set $location = "fhParentsBath">>
<<else>>
<<set $location = "fhUpperBath">>
<</if>>
<<set _doorClip = random(1, 3)>>
<<if _doorClip === 1>>
<<set _doorVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/doorAjar1.mp4">>
<<set _doorVideoSize = "100%">>
<<elseif _doorClip === 2>>
<<set _doorVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/doorAjar2.mp4">>
<<set _doorVideoSize = "100%">>
<<else>>
<<set _doorVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/doorAjar3.mp4">>
<<set _doorVideoSize = "100%">>
<</if>>
<<set _canPeek = ($corruption >= 2) && (_lustLevel >= 2) && (_lust >= 5)>>
<<set _peekContentAllowed = false>>
<<if _cid === "mother">>
<<set _peekContentAllowed = ($contentPreferences.incest !== false) && ($contentPreferences.femaleSexual !== false)>>
<<elseif _cid === "father" || _cid === "brother">>
<<set _peekContentAllowed = ($contentPreferences.incest !== false) && ($contentPreferences.maleSexual !== false)>>
<</if>>
<<set _fatherFirstPeekSeen = !!$flags.fatherShowerPeekFirstSeen>>
<<set _motherFirstPeekSeen = !!$flags.motherShowerPeekFirstSeen>>
<<set _brotherFirstPeekSeen = !!$flags.brotherShowerPeekFirstSeen>>
<<set _introVar = random(1, 3)>>
<</silently>>
<<if _introVar === 1>>
<<narrative "Bathroom">>
The door isn't quite latched a thin strip of light and steam at the edge. Inside, the shower runs steady; <<print _first>> has to be the one in there.
<</narrative>>
<<elseif _introVar === 2>>
<<narrative "Bathroom">>
You almost walk past, then stop. The door hangs open a finger's width. Water drums on tile behind it, and you know <<print _first>>'s routine well enough: that's a shower, not a sink.
<</narrative>>
<<else>>
<<narrative "Bathroom">>
A draft of warm, damp air slips into the hall. The bathroom door is barely cracked; from the gap comes the hiss of the spray and, faintly, movement. <<print _first>> is almost certainly behind it.
<</narrative>>
<</if>>
<<vid _doorVideo _doorVideoSize>>
<div class="location-actions">
<<if _canPeek && _peekContentAllowed>>
<<if _cid === "mother">>
<<if !_motherFirstPeekSeen>>
<<btn "Peek inside" "showerEncounter_peek_Mother_first">><</btn>>
<<else>>
<<btn "Peek inside" "showerEncounter_peek_Mother">><</btn>>
<</if>>
<<elseif _cid === "father">>
<<if !_fatherFirstPeekSeen>>
<<btn "Peek inside" "showerEncounter_peek_Father_first">><</btn>>
<<else>>
<<btn "Peek inside" "showerEncounter_peek_Father">><</btn>>
<</if>>
<<elseif _cid === "brother">>
<<if !_brotherFirstPeekSeen>>
<<btn "Peek inside" "showerEncounter_peek_Brother_first">><</btn>>
<<else>>
<<btn "Peek inside" "showerEncounter_peek_Brother">><</btn>>
<</if>>
<</if>>
<<elseif !_peekContentAllowed>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Turn on Incest and the matching sexual content option in Settings (Content Preferences) to enable this."><i class="icon icon-lock icon-12"></i> Peek inside</span>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Requires corruption 2, lust level 2 or higher, and at least 5 lust with this person."><i class="icon icon-lock icon-12"></i> Peek inside</span>
<</if>>
<<btn "Walk away" _return "secondary">><<unset State.temporary.peekShowerTarget>><<unset $showerEncounterReturnLoc>><<unset $interactionEntryPassage>><</btn>>
</div>
<</nobr>><<set $showerEncounterTarget = "brother">>
<<if !$showerEncounterReturnLoc>>
<<set $showerEncounterReturnLoc = "fhUpperstairs">>
<</if>>
<<goto "showerEncounter">><<set $showerEncounterTarget = "father">>
<<if !$showerEncounterReturnLoc>>
<<set $showerEncounterReturnLoc = "fhParentsRoom">>
<</if>>
<<goto "showerEncounter">><<set $showerEncounterTarget = "mother">>
<<if !$showerEncounterReturnLoc>>
<<set $showerEncounterReturnLoc = "fhParentsRoom">>
<</if>>
<<goto "showerEncounter">><<nobr>>
<<silently>>
<<set _cid = "brother">>
<<set _char = $characters.brother>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhUpperstairs">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.maleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhUpperBath">>
<<set _lust = (_char.stats && typeof _char.stats.lust === "number") ? _char.stats.lust : 0>>
<<set _lustLevel = (_char.stats && typeof _char.stats.lustLevel === "number") ? _char.stats.lustLevel : 1>>
<<set _canKeepWatching = (_lustLevel >= 2) && (_lust >= 10)>>
<<set _peekVar = random(1, 3)>>
<<set _videoVar = random(1, 3)>>
<<if _videoVar === 1>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherFirstScene.mp4">>
<<set _peekVideoSize = "100%">>
<<elseif _videoVar === 2>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherShower1-1.mp4">>
<<set _peekVideoSize = "100%">>
<<else>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherShower1-2.mp4">>
<<set _peekVideoSize = "100%">>
<</if>>
<<gainStat "stress" 2>>
<<gainStat "arousal" 4>>
<<recalculateStats>>
<</silently>>
<<if _peekVar === 1>>
<<narrative "Upstairs Bathroom">>
You ease the door wider only enough to slip through, slow and soundless. The fan and the shower swallow any creak. Steam hits your face; you hug the wall where the tile stays dry.
<</narrative>>
<<narrative>>
<<print _char.firstName>> is square to the spray, his back to you, head tipped under the water. From here he can't see the doorway. He has no reason to think anyone's behind him.
<</narrative>>
<<elseif _peekVar === 2>>
<<narrative "Upstairs Bathroom">>
You nudge the door a little farther open. Just enough for a look. Steam drifts into the hall while the shower and the fan cover whatever sound you make.
<</narrative>>
<<narrative>>
Your brother stands with his back to you beneath the spray, head angled down. His hands move slowly over his arms and neck. He never glances toward the doorway. There's no reason for him to think anyone is there.
<</narrative>>
<<else>>
<<narrative "Upstairs Bathroom">>
You ease the door wider and hold still. Warm damp air spills through the gap and settles against your skin while you stay close to the wall and look in.
<</narrative>>
<<narrative>>
Your brother is turned away from you, shoulders slick with water. He stays in his own routine, unaware of the doorway, unaware of you.
<</narrative>>
<</if>>
<<vid _peekVideo _peekVideoSize>>
<div class="location-actions">
<<if _canKeepWatching>>
<<btn "Keep watching" "showerEncounter_peek_Brother_continue">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Requires lust level 2 and at least 10 lust with your brother."><i class="icon icon-lock icon-12"></i> Keep watching</span>
<</if>>
<<btn "Step away" "fhUpperstairs">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _char = $characters.brother>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhUpperstairs">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.maleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhUpperBath">>
<<set _videoVar = random(1, 5)>>
<<if _videoVar === 1>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherShower2-1.mp4">>
<<set _peekVideoSize = "50%">>
<<elseif _videoVar === 2>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherShower2-2.mp4">>
<<set _peekVideoSize = "100%">>
<<elseif _videoVar === 3>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherShower2-3.mp4">>
<<set _peekVideoSize = "100%">>
<<elseif _videoVar === 4>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherShower2-4.mp4">>
<<set _peekVideoSize = "100%">>
<<else>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherShower2-5.mp4">>
<<set _peekVideoSize = "100%">>
<</if>>
<<gainStat "stress" 2>>
<<gainStat "arousal" 3>>
<<recalculateStats>>
<</silently>>
<<narrative "Upstairs Bathroom">>
You don't move. The steam thins for a moment and the angle changes.
<</narrative>>
<<vid _peekVideo _peekVideoSize>>
<<narrative>>
You see it clearly now.
<</narrative>>
<<narrative>>
<<print _char.firstName>>'s cock.
<</narrative>>
<<dialog "player">>
(Inner voice)
Oh my god...
<<print _char.firstName>>'s cock...
<</dialog>>
<<narrative>>
You snort before you can stop yourself. Then freeze.
<</narrative>>
<<narrative>>
But you still don't move.
<</narrative>>
<<dialog "player">>
(Inner voice)
I shouldn't be seeing this...
<</dialog>>
<<narrative>>
He keeps rinsing, completely unaware. Water runs down his body.
<</narrative>>
<<narrative>>
You're still standing there.
<</narrative>>
<<dialog "player">>
(Inner voice)
Go...
Just go...
...
You're not leaving.
<</dialog>>
<div class="location-actions">
<<btn "Back" "fhUpperstairs">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
<<btn "Continue" _return>><<unset State.temporary.peekShowerTarget>><<unset $showerEncounterReturnLoc>><<unset $interactionEntryPassage>><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _cid = "brother">>
<<set _char = $characters.brother>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhUpperstairs">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.maleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhUpperBath">>
<<set _lust = (_char.stats && typeof _char.stats.lust === "number") ? _char.stats.lust : 0>>
<<set _lustLevel = (_char.stats && typeof _char.stats.lustLevel === "number") ? _char.stats.lustLevel : 1>>
<<set _canKeepWatching = (_lustLevel >= 2) && (_lust >= 10)>>
<<set $flags.brotherShowerPeekFirstSeen = true>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/brother/brotherFirstScene.mp4">>
<<set _peekVideoSize = "100%">>
<<gainStat "stress" 2>>
<<gainStat "arousal" 4>>
<<recalculateStats>>
<</silently>>
<<narrative "Upstairs Bathroom">>
You ease the door wider.
<</narrative>>
<<narrative>>
Just enough to look. A second, maybe. Then you'd step away.
<</narrative>>
<<narrative>>
But the second you see him, your body stops.
<</narrative>>
<<narrative>>
His back is to you. Water slips from his shoulders down his waist, over the curve of his hips. He has no idea anyone is there.
<</narrative>>
<<narrative>>
You don't look away.
<</narrative>>
<<vid _peekVideo _peekVideoSize>>
<<dialog "player">>
(Inner voice)
Okay.
You saw enough.
Look away.
<</dialog>>
<<narrative>>
But you don't.
<</narrative>>
<<narrative>>
Your hand locks around the edge of the door. Your pulse turns heavy and fast. You stand there so still it feels like even breathing might give you away.
<</narrative>>
<<dialog "player">>
(Inner voice)
What am I doing?
Why am I still here?
<</dialog>>
<<narrative>>
The hiss of the shower fills the room. His soapy hands move over his arms, his neck. He shifts his weight just a little.
<</narrative>>
<<narrative>>
One more second, you tell yourself.
<</narrative>>
<<narrative>>
Just one.
<</narrative>>
<<narrative>>
The second stretches.
<</narrative>>
<<narrative>>
That's the part that lands hardest.
<</narrative>>
<<narrative>>
Not what you're seeing.
<</narrative>>
<<narrative>>
The fact that you should have stepped back already, and didn't.
<</narrative>>
<<dialog "player">>
(Inner voice)
Go.
Right now.
If you stay, then you're really choosing this.
<</dialog>>
<<narrative>>
That thought drops cold into your stomach.
<</narrative>>
<<narrative>>
This isn't an accident anymore.
<</narrative>>
<<narrative>>
It's the few seconds you stayed after you knew exactly what you were doing.
<</narrative>>
<<narrative>>
Your hand is still on the door. Part of you wants to pull back into the hall before the floor creaks or he turns even slightly. The other part hasn't moved.
<</narrative>>
<<narrative>>
You know you should step away.
<</narrative>>
<<narrative>>
You just haven't done it yet.
<</narrative>>
<div class="location-actions">
<<if _canKeepWatching>>
<<btn "Keep watching" "showerEncounter_peek_Brother_continue">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Requires lust level 2 and at least 10 lust with your brother."><i class="icon icon-lock icon-12"></i> Keep watching</span>
<</if>>
<<btn "Step away" "fhUpperstairs">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _cid = "father">>
<<set _char = $characters.father>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhParentsRoom">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.maleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhParentsBath">>
<<set _lust = (_char.stats && typeof _char.stats.lust === "number") ? _char.stats.lust : 0>>
<<set _lustLevel = (_char.stats && typeof _char.stats.lustLevel === "number") ? _char.stats.lustLevel : 1>>
<<set _canKeepWatching = (_lustLevel >= 2) && (_lust >= 10)>>
<<set _peekVar = random(1, 3)>>
<<set _videoVar = random(1, 2)>>
<<if _videoVar === 1>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/father/dadFirstScene.mp4">>
<<set _peekVideoSize = "50%">>
<<else>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/father/dadShower1-2.mp4">>
<<set _peekVideoSize = "100%">>
<</if>>
<<gainStat "stress" 2>>
<<gainStat "arousal" 4>>
<<recalculateStats>>
<</silently>>
<<if _peekVar === 1>>
<<narrative "Parents Bathroom">>
You ease the door wider only enough to slip through, slow and soundless. The fan and the shower swallow any creak. Steam hits your face; you hug the wall where the tile stays dry.
<</narrative>>
<<narrative>>
<<print _char.firstName>> is square to the spray, his back to you, head tipped under the water. From here he can't see the doorway. He has no reason to think anyone's behind him.
<</narrative>>
<<elseif _peekVar === 2>>
<<narrative "Parents Bathroom">>
You nudge the door a little farther open. Just enough for a look. Steam drifts into the hall while the shower and the fan cover whatever sound you make.
<</narrative>>
<<narrative>>
Your father stands with his back to you beneath the spray, head angled down. His hands move slowly over his arms and neck. He never glances toward the doorway. There's no reason for him to think anyone is there.
<</narrative>>
<<else>>
<<narrative "Parents Bathroom">>
You ease the door wider and hold still. Warm damp air spills through the gap and settles against your skin while you stay close to the wall and look in.
<</narrative>>
<<narrative>>
Your father is turned away from you, broad back under the stream, shoulders slick with water. He stays in his own routine, unaware of the doorway, unaware of you.
<</narrative>>
<</if>>
<<vid _peekVideo _peekVideoSize>>
<div class="location-actions">
<<if _canKeepWatching>>
<<btn "Keep watching" "showerEncounter_peek_Father_continue">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Requires lust level 2 and at least 10 lust with your father."><i class="icon icon-lock icon-12"></i> Keep watching</span>
<</if>>
<<btn "Step away" "fhParentsRoom">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _char = $characters.father>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhParentsRoom">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.maleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhParentsBath">>
<<set _contVar = random(1, 3)>>
<<set _videoVar = random(1, 3)>>
<<if _videoVar === 1>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/father/dadShower2-1.mp4">>
<<set _peekVideoSize = "50%">>
<<elseif _videoVar === 2>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/father/dadShower2-2.mp4">>
<<set _peekVideoSize = "50%">>
<<else>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/father/dadShower2-3.mp4">>
<<set _peekVideoSize = "50%">>
<</if>>
<<gainStat "stress" 2>>
<<gainStat "arousal" 3>>
<<recalculateStats>>
<</silently>>
<<if _contVar === 1>>
<<narrative "Parents Bathroom">>
You don't leave. The steam suddenly clears for a moment and the angle changes.
<</narrative>>
<<vid _peekVideo _peekVideoSize>>
<<narrative>>
You see it clearly now.
<</narrative>>
<<narrative>>
Dad's cock.
<</narrative>>
<<dialog "player">>
(Inner voice)
Oh my god...
Dad's cock...
<</dialog>>
<<narrative>>
You go still. Your mouth tastes wrong.
<</narrative>>
<<narrative>>
But you still don't move.
<</narrative>>
<<dialog "player">>
(Inner voice)
I shouldn't be seeing this...
<</dialog>>
<<narrative>>
Dad keeps rinsing himself, completely unaware. Water runs down his body.
<</narrative>>
<<narrative>>
You're still standing there.
<</narrative>>
<<dialog "player">>
(Inner voice)
Go...
Just go...
...
You're not leaving.
<</dialog>>
<<elseif _contVar === 2>>
<<narrative "Parents Bathroom">>
You stay. The fog on the glass shifts in a wave and everything becomes clear.
<</narrative>>
<<vid _peekVideo _peekVideoSize>>
<<narrative>>
Dad's cock is right in front of you.
<</narrative>>
<<narrative>>
Fully visible.
<</narrative>>
<<dialog "player">>
(Inner voice)
Oh shit...
I really saw it.
<</dialog>>
<<narrative>>
Your heart is pounding so hard it feels like it might burst. Your whole body freezes.
<</narrative>>
<<narrative>>
But your feet won't move.
<</narrative>>
<<dialog "player">>
(Inner voice)
This is so wrong...
So wrong.
<</dialog>>
<<narrative>>
Dad is still under the shower, totally oblivious. The sound of water continues.
<</narrative>>
<<narrative>>
You're still watching through the doorway.
<</narrative>>
<<dialog "player">>
(Inner voice)
Leave...
Right now...
...
You're not leaving.
<</dialog>>
<<else>>
<<narrative "Parents Bathroom">>
You stay one more second. Then the steam clears completely and the angle shifts.
<</narrative>>
<<vid _peekVideo _peekVideoSize>>
<<narrative>>
Dad's cock is clearly visible.
<</narrative>>
<<dialog "player">>
(Inner voice)
Oh god...
Dad's...
<</dialog>>
<<narrative>>
Your stomach drops. It feels like your insides just flipped.
<</narrative>>
<<narrative>>
But you can't look away.
<</narrative>>
<<dialog "player">>
(Inner voice)
That's enough...
That's enough...
<</dialog>>
<<narrative>>
Dad keeps showering, back still turned, completely unaware.
<</narrative>>
<<narrative>>
You're still in the same spot, still watching.
<</narrative>>
<<dialog "player">>
(Inner voice)
Go...
Please go...
...
You're not leaving.
<</dialog>>
<</if>>
<div class="location-actions">
<<btn "Back" "fhParentsRoom">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
<<btn "Continue" _return>><<unset State.temporary.peekShowerTarget>><<unset $showerEncounterReturnLoc>><<unset $interactionEntryPassage>><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _cid = "father">>
<<set _char = $characters.father>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhParentsRoom">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.maleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhParentsBath">>
<<set _lust = (_char.stats && typeof _char.stats.lust === "number") ? _char.stats.lust : 0>>
<<set _lustLevel = (_char.stats && typeof _char.stats.lustLevel === "number") ? _char.stats.lustLevel : 1>>
<<set _canKeepWatching = (_lustLevel >= 2) && (_lust >= 10)>>
<<set $flags.fatherShowerPeekFirstSeen = true>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/father/dadFirstScene.mp4">>
<<set _peekVideoSize = "50%">>
<<gainStat "stress" 2>>
<<gainStat "arousal" 4>>
<<recalculateStats>>
<</silently>>
<<narrative "Parents Bathroom">>
You ease the door wider.
<</narrative>>
<<narrative>>
Just enough to look. A second, maybe. Then you'd step away.
<</narrative>>
<<narrative>>
But the second you see him, your body stops.
<</narrative>>
<<narrative>>
His back is to you. Water slips from his shoulders down his waist, over the curve of his hips. He has no idea anyone is there.
<</narrative>>
<<narrative>>
You don't look away.
<</narrative>>
<<vid _peekVideo _peekVideoSize>>
<<dialog "player">>
(Inner voice)
Okay.
You saw enough.
Look away.
<</dialog>>
<<narrative>>
But you don't.
<</narrative>>
<<narrative>>
Your hand locks around the edge of the door. Your pulse turns heavy and fast. You stand there so still it feels like even breathing might give you away.
<</narrative>>
<<dialog "player">>
(Inner voice)
What am I doing?
Why am I still here?
<</dialog>>
<<narrative>>
The hiss of the shower fills the room. His soapy hands move over his arms, his neck. He shifts his weight just a little.
<</narrative>>
<<narrative>>
One more second, you tell yourself.
<</narrative>>
<<narrative>>
Just one.
<</narrative>>
<<narrative>>
The second stretches.
<</narrative>>
<<narrative>>
That's the part that lands hardest.
<</narrative>>
<<narrative>>
Not what you're seeing.
<</narrative>>
<<narrative>>
The fact that you should have stepped back already, and didn't.
<</narrative>>
<<dialog "player">>
(Inner voice)
Go.
Right now.
If you stay, then you're really choosing this.
<</dialog>>
<<narrative>>
That thought drops cold into your stomach.
<</narrative>>
<<narrative>>
This isn't an accident anymore.
<</narrative>>
<<narrative>>
It's the few seconds you stayed after you knew exactly what you were doing.
<</narrative>>
<<narrative>>
Your hand is still on the door. Part of you wants to pull back into the hall before the floor creaks or he turns even slightly. The other part hasn't moved.
<</narrative>>
<<narrative>>
You know you should step away.
<</narrative>>
<<narrative>>
You just haven't done it yet.
<</narrative>>
<div class="location-actions">
<<if _canKeepWatching>>
<<btn "Keep watching" "showerEncounter_peek_Father_continue">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Requires lust level 2 and at least 10 lust with your father."><i class="icon icon-lock icon-12"></i> Keep watching</span>
<</if>>
<<btn "Step away" "fhParentsRoom">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _cid = "mother">>
<<set _char = $characters.mother>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhParentsRoom">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.femaleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhParentsBath">>
<<set _lust = (_char.stats && typeof _char.stats.lust === "number") ? _char.stats.lust : 0>>
<<set _lustLevel = (_char.stats && typeof _char.stats.lustLevel === "number") ? _char.stats.lustLevel : 1>>
<<set _canKeepWatching = (_lustLevel >= 2) && (_lust >= 10)>>
<<set _peekVar = random(1, 3)>>
<<set _videoVar = random(1, 4)>>
<<if _videoVar === 1>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower1-1.mp4">>
<<set _peekVideoSize = "100%">>
<<elseif _videoVar === 2>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower1-2.mp4">>
<<set _peekVideoSize = "50%">>
<<elseif _videoVar === 3>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower1-3.mp4">>
<<set _peekVideoSize = "50%">>
<<else>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower1-4.mp4">>
<<set _peekVideoSize = "50%">>
<</if>>
<<gainStat "stress" 2>>
<<gainStat "arousal" 4>>
<<recalculateStats>>
<</silently>>
<<if _peekVar === 1>>
<<narrative "Parents Bathroom">>
You ease the door wider only enough to slip through, slow and soundless. The fan and the shower swallow any creak. Steam hits your face; you hug the wall where the tile stays dry.
<</narrative>>
<<narrative>>
<<print _char.firstName>> is square to the spray, her back to you, head tipped under the water. From here she can't see the doorway. She has no reason to think anyone's behind her.
<</narrative>>
<<elseif _peekVar === 2>>
<<narrative "Parents Bathroom">>
You nudge the door a little farther open. Just enough for a look. Steam drifts into the hall while the shower and the fan cover whatever sound you make.
<</narrative>>
<<narrative>>
Your mother stands with her back to you beneath the spray, head angled down. Her hands move slowly over her arms and neck. She never glances toward the doorway. There's no reason for her to think anyone is there.
<</narrative>>
<<else>>
<<narrative "Parents Bathroom">>
You ease the door wider and hold still. Warm damp air spills through the gap and settles against your skin while you stay close to the wall and look in.
<</narrative>>
<<narrative>>
Your mother is turned away from you, shoulders slick with water. She stays in her own routine, unaware of the doorway, unaware of you.
<</narrative>>
<</if>>
<<vid _peekVideo _peekVideoSize>>
<div class="location-actions">
<<if _canKeepWatching>>
<<btn "Keep watching" "showerEncounter_peek_Mother_continue">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Requires lust level 2 and at least 10 lust with your mother."><i class="icon icon-lock icon-12"></i> Keep watching</span>
<</if>>
<<btn "Step away" "fhParentsRoom">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _char = $characters.mother>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhParentsRoom">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.femaleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhParentsBath">>
<<set _videoVar = random(1, 5)>>
<<if _videoVar === 1>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower2-1.mp4">>
<<set _peekVideoSize = "50%">>
<<elseif _videoVar === 2>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower2-2.mp4">>
<<set _peekVideoSize = "100%">>
<<elseif _videoVar === 3>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower2-3.mp4">>
<<set _peekVideoSize = "100%">>
<<elseif _videoVar === 4>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower2-4.mp4">>
<<set _peekVideoSize = "100%">>
<<else>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momShower2-5.mp4">>
<<set _peekVideoSize = "100%">>
<</if>>
<<gainStat "stress" 2>>
<<gainStat "arousal" 3>>
<<recalculateStats>>
<</silently>>
<<narrative "Parents Bathroom">>
You don't move. The steam thins for a moment and the angle changes.
<</narrative>>
<<vid _peekVideo _peekVideoSize>>
<<narrative>>
You see it clearly now.
<</narrative>>
<<narrative>>
<<print _char.firstName>>'s breasts and body.
<</narrative>>
<<dialog "player">>
(Inner voice)
Oh my god...
<<print _char.firstName>>'s body...
<</dialog>>
<<narrative>>
Your ears go hot. You stop breathing without meaning to.
<</narrative>>
<<narrative>>
But you still don't move.
<</narrative>>
<<dialog "player">>
(Inner voice)
I shouldn't be seeing this...
<</dialog>>
<<narrative>>
She keeps rinsing, completely unaware. Water runs down her body.
<</narrative>>
<<narrative>>
You're still standing there.
<</narrative>>
<<dialog "player">>
(Inner voice)
Go...
Just go...
...
You're not leaving.
<</dialog>>
<div class="location-actions">
<<btn "Back" "fhParentsRoom">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
<<btn "Continue" _return>><<unset State.temporary.peekShowerTarget>><<unset $showerEncounterReturnLoc>><<unset $interactionEntryPassage>><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set _cid = "mother">>
<<set _char = $characters.mother>>
<<set _return = $showerEncounterReturnLoc>>
<<if !_return>>
<<set _return = "fhParentsRoom">>
<</if>>
<<if ($contentPreferences.incest === false) || ($contentPreferences.femaleSexual === false)>>
<<goto _return>>
<</if>>
<<set $location = "fhParentsBath">>
<<set _lust = (_char.stats && typeof _char.stats.lust === "number") ? _char.stats.lust : 0>>
<<set _lustLevel = (_char.stats && typeof _char.stats.lustLevel === "number") ? _char.stats.lustLevel : 1>>
<<set _canKeepWatching = (_lustLevel >= 2) && (_lust >= 10)>>
<<set $flags.motherShowerPeekFirstSeen = true>>
<<set _peekVideo = "assets/content/scenes/maplewood/familyHouse/showerEncounter/mother/momFirstScene.mp4">>
<<set _peekVideoSize = "100%">>
<<gainStat "stress" 2>>
<<gainStat "arousal" 4>>
<<recalculateStats>>
<</silently>>
<<narrative "Parents Bathroom">>
You ease the door wider.
<</narrative>>
<<narrative>>
Just enough to look. A second, maybe. Then you'd step away.
<</narrative>>
<<narrative>>
But the second you see her, your body stops.
<</narrative>>
<<narrative>>
Her back is to you. Water slips from her shoulders down her waist, over the curve of her hips. She has no idea anyone is there.
<</narrative>>
<<narrative>>
You don't look away.
<</narrative>>
<<vid _peekVideo _peekVideoSize>>
<<dialog "player">>
(Inner voice)
Okay.
You saw enough.
Look away.
<</dialog>>
<<narrative>>
But you don't.
<</narrative>>
<<narrative>>
Your hand locks around the edge of the door. Your pulse turns heavy and fast. You stand there so still it feels like even breathing might give you away.
<</narrative>>
<<dialog "player">>
(Inner voice)
What am I doing?
Why am I still here?
<</dialog>>
<<narrative>>
The hiss of the shower fills the room. Her soapy hands move over her arms, her neck. She shifts her weight just a little.
<</narrative>>
<<narrative>>
One more second, you tell yourself.
<</narrative>>
<<narrative>>
Just one.
<</narrative>>
<<narrative>>
The second stretches.
<</narrative>>
<<narrative>>
That's the part that lands hardest.
<</narrative>>
<<narrative>>
Not what you're seeing.
<</narrative>>
<<narrative>>
The fact that you should have stepped back already, and didn't.
<</narrative>>
<<dialog "player">>
(Inner voice)
Go.
Right now.
If you stay, then you're really choosing this.
<</dialog>>
<<narrative>>
That thought drops cold into your stomach.
<</narrative>>
<<narrative>>
This isn't an accident anymore.
<</narrative>>
<<narrative>>
It's the few seconds you stayed after you knew exactly what you were doing.
<</narrative>>
<<narrative>>
Your hand is still on the door. Part of you wants to pull back into the hall before the floor creaks or she turns even slightly. The other part hasn't moved.
<</narrative>>
<<narrative>>
You know you should step away.
<</narrative>>
<<narrative>>
You just haven't done it yet.
<</narrative>>
<div class="location-actions">
<<if _canKeepWatching>>
<<btn "Keep watching" "showerEncounter_peek_Mother_continue">><</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked" data-tooltip="Requires lust level 2 and at least 10 lust with your mother."><i class="icon icon-lock icon-12"></i> Keep watching</span>
<</if>>
<<btn "Step away" "fhParentsRoom">>
<<unset State.temporary.peekShowerTarget>>
<<unset $showerEncounterReturnLoc>>
<<unset $interactionEntryPassage>>
<</btn>>
</div>
<</nobr>><<nobr>>
<<run $('body').addClass('fullscreen-centered')>>
<<if ndef $prologueSelections>>
<<set $prologueSelections = {
early: null,
childhood: null,
formative: null,
adolescent: null,
coming: null
}>>
<<set $prologueBonuses = {}>>
<</if>>
<div class="page-wrapper">
<div class="prologue-page skip-prologue">
<div class="prologue-content-area" style="max-width: 1000px;">
<div class="prologue-header">
<h1 class="prologue-title">Quick Start</h1>
<div class="prologue-header-line"></div>
<p class="prologue-subtitle">Choose one trait from each life stage to shape your character.</p>
</div>
<!-- Early Years (0-5) -->
<div class="skip-section" data-section="early">
<div class="skip-section-header">
<span class="skip-section-title">Early Years (0-5)</span>
<span class="skip-section-status" id="early-status">Select one</span>
</div>
<div class="skip-cards">
<div class="skip-card" data-section="early" data-title="Your Own Space" data-effect="+15% Focus" data-stat="focus" data-value="1.15">
<div class="skip-card-title">Your Own Space</div>
<div class="skip-card-effect">+15% Focus</div>
</div>
<div class="skip-card" data-section="early" data-title="Dad's Kingdom" data-effect="+15% Fitness" data-stat="fitness" data-value="1.15">
<div class="skip-card-title">Dad's Kingdom</div>
<div class="skip-card-effect">+15% Fitness</div>
</div>
<div class="skip-card" data-section="early" data-title="Mom's Special Time" data-effect="+15% Intelligence" data-stat="intelligence" data-value="1.15">
<div class="skip-card-title">Mom's Special Time</div>
<div class="skip-card-effect">+15% Intelligence</div>
</div>
<div class="skip-card" data-section="early" data-title="Family Pride" data-effect="+15% Charisma" data-stat="charisma" data-value="1.15">
<div class="skip-card-title">Family Pride</div>
<div class="skip-card-effect">+15% Charisma</div>
</div>
</div>
</div>
<!-- Childhood (6-9) -->
<div class="skip-section" data-section="childhood">
<div class="skip-section-header">
<span class="skip-section-title">Childhood (6-9)</span>
<span class="skip-section-status" id="childhood-status">Select one</span>
</div>
<div class="skip-cards">
<div class="skip-card" data-section="childhood" data-title="Dad's Lessons" data-effect="+15% Willpower" data-stat="willpower" data-value="1.15">
<div class="skip-card-title">Dad's Lessons</div>
<div class="skip-card-effect">+15% Willpower</div>
</div>
<div class="skip-card" data-section="childhood" data-title="Mom's Encouragement" data-effect="+15% Creativity" data-stat="creativity" data-value="1.15">
<div class="skip-card-title">Mom's Encouragement</div>
<div class="skip-card-effect">+15% Creativity</div>
</div>
<div class="skip-card" data-section="childhood" data-title="Family Dinner Talks" data-effect="+15% Intelligence" data-stat="mental" data-value="1.15">
<div class="skip-card-title">Family Dinner Talks</div>
<div class="skip-card-effect">+15% Intelligence</div>
</div>
<div class="skip-card" data-section="childhood" data-title="Mom's Social Butterfly" data-effect="+15% Charisma" data-stat="social" data-value="1.15">
<div class="skip-card-title">Mom's Social Butterfly</div>
<div class="skip-card-effect">+15% Charisma</div>
</div>
</div>
</div>
<!-- Formative (10-12) -->
<div class="skip-section" data-section="formative">
<div class="skip-section-header">
<span class="skip-section-title">Formative Years (10-12)</span>
<span class="skip-section-status" id="formative-status">Select one</span>
</div>
<div class="skip-cards">
<div class="skip-card" data-section="formative" data-title="Dad's Coaching" data-effect="+15% Fitness" data-stat="fitness" data-value="1.15">
<div class="skip-card-title">Dad's Coaching</div>
<div class="skip-card-effect">+15% Fitness</div>
</div>
<div class="skip-card" data-section="formative" data-title="Mom's Pride" data-effect="+15% Intelligence" data-stat="intelligence" data-value="1.15">
<div class="skip-card-title">Mom's Pride</div>
<div class="skip-card-effect">+15% Intelligence</div>
</div>
<div class="skip-card" data-section="formative" data-title="Finding Your Thing" data-effect="+15% Physical Skills" data-stat="physical" data-value="1.15">
<div class="skip-card-title">Finding Your Thing</div>
<div class="skip-card-effect">+15% Physical Skills</div>
</div>
<div class="skip-card" data-section="formative" data-title="Your Own Path" data-effect="+15% Creative Skills" data-stat="creativity" data-value="1.15">
<div class="skip-card-title">Your Own Path</div>
<div class="skip-card-effect">+15% Creative Skills</div>
</div>
</div>
</div>
<!-- Adolescent (13-15) -->
<div class="skip-section" data-section="adolescent">
<div class="skip-section-header">
<span class="skip-section-title">Adolescent Years (13-15)</span>
<span class="skip-section-status" id="adolescent-status">Select one</span>
</div>
<div class="skip-cards">
<div class="skip-card" data-section="adolescent" data-title="Mom's Tension" data-effect="+15% Beauty" data-stat="beauty" data-value="1.15">
<div class="skip-card-title">Mom's Tension</div>
<div class="skip-card-effect">+15% Beauty</div>
</div>
<div class="skip-card" data-section="adolescent" data-title="Dad's Quiet Support" data-effect="+15% Social Skills" data-stat="social" data-value="1.15">
<div class="skip-card-title">Dad's Quiet Support</div>
<div class="skip-card-effect">+15% Social Skills</div>
</div>
<div class="skip-card" data-section="adolescent" data-title="Your Space" data-effect="+15% Technical Skills" data-stat="technical" data-value="1.15">
<div class="skip-card-title">Your Space</div>
<div class="skip-card-effect">+15% Technical Skills</div>
</div>
<div class="skip-card" data-section="adolescent" data-title="Beyond Family" data-effect="+15% Willpower" data-stat="willpower" data-value="1.15">
<div class="skip-card-title">Beyond Family</div>
<div class="skip-card-effect">+15% Willpower</div>
</div>
</div>
</div>
<!-- Coming of Age (16-17) -->
<div class="skip-section" data-section="coming">
<div class="skip-section-header">
<span class="skip-section-title">Coming of Age (16-17)</span>
<span class="skip-section-status" id="coming-status">Select one</span>
</div>
<div class="skip-cards">
<div class="skip-card" data-section="coming" data-title="Coming Into Your Own" data-effect="+15% Beauty" data-stat="beauty" data-value="1.15">
<div class="skip-card-title">Coming Into Your Own</div>
<div class="skip-card-effect">+15% Beauty</div>
</div>
<div class="skip-card" data-section="coming" data-title="Understanding Desire" data-effect="+15% Charisma" data-stat="charisma" data-value="1.15">
<div class="skip-card-title">Understanding Desire</div>
<div class="skip-card-effect">+15% Charisma</div>
</div>
<div class="skip-card" data-section="coming" data-title="Breaking Away" data-effect="+15% Practical Skills" data-stat="practical" data-value="1.15">
<div class="skip-card-title">Breaking Away</div>
<div class="skip-card-effect">+15% Practical Skills</div>
</div>
<div class="skip-card" data-section="coming" data-title="Physical Awakening" data-effect="+15% Physical Skills" data-stat="physical" data-value="1.15">
<div class="skip-card-title">Physical Awakening</div>
<div class="skip-card-effect">+15% Physical Skills</div>
</div>
</div>
</div>
<!-- Buttons -->
<div class="prologue-btn-row" style="margin-top: 2rem;">
<<link '<span class="prologue-btn prologue-btn-secondary">Back</span>' "confirmationPage">><</link>>
<span id="skip-start" class="prologue-btn prologue-btn-primary disabled">Start Game</span>
</div>
</div>
</div>
</div>
<</nobr>>
<<script>>
$(document).ready(function() {
var selections = State.variables.prologueSelections;
var sections = ['early', 'childhood', 'formative', 'adolescent', 'coming'];
function updateStartButton() {
var allSelected = sections.every(function(s) {
return selections[s] !== null;
});
if (allSelected) {
$('#skip-start')
.removeClass('disabled')
.css('cursor', 'pointer')
.off('click')
.on('click', function() {
// Set game start time
State.variables.timeSys.year = 2025;
State.variables.timeSys.month = 8;
State.variables.timeSys.day = 15;
State.variables.timeSys.weekday = 4;
State.variables.timeSys.hour = 15;
State.variables.timeSys.minute = 22;
// Initialize wardrobe if needed
if (!State.variables.wardrobe || !State.variables.wardrobe.owned || State.variables.wardrobe.owned.length === 0) {
State.variables.wardrobe = {
owned: ["tshirt_white_owned", "tshirt_blue_owned", "tshirt_gray_owned", "jeans_blue_owned", "leggings_black_owned", "dress_summer_owned", "socks_white_owned", "socks_black_owned", "sneakers_flat_owned", "sneakers_girly_owned", "bra_sport_white_owned", "bra_black_owned", "panties_white_owned", "panties_pink_owned", "pajama_color_owned"],
outfits: [null, null, null, null, null],
equipped: {
top: "tshirt_white_owned",
bottom: "jeans_blue_owned",
shoes: "sneakers_flat_owned",
socks: "socks_white_owned",
panty: "panties_white_owned",
bra: "bra_sport_white_owned"
}
};
}
// Re-enable UI
State.variables.hideTopbarNav = false;
State.variables.hideTopbarTimebox = false;
State.variables.hideTopbarNotifications = false;
State.variables.hideRightbar = false;
State.variables.hideTopbar = false;
State.variables.discoveredStoreCorner = true;
$('body').removeClass('fullscreen-centered');
// Mark prologue as complete
State.variables.prologue_complete = true;
// Appearance: worst values at game start; only hair "combed" (hairCare 50, hairMessiness 0)
if (!State.variables.appearance) State.variables.appearance = {};
State.variables.appearance.hairCare = 50;
State.variables.appearance.hairMessiness = 0;
State.variables.appearance.hairCombed = 1;
State.variables.appearance.faceCare = 0;
State.variables.appearance.dentalCare = 0;
if (typeof $ !== 'undefined' && $.wiki) $.wiki('<<recalculateStats>>');
// Ensure map is hidden until Marcus gives it
State.variables.hideRightbarMap = true;
// Mother gives money for groceries
$.wiki('<<earnMoney 20>>');
// Start first quest
if (typeof Macro !== 'undefined' && Macro.get('startQuest')) {
$.wiki('<<startQuest "first_shopping">>');
}
// Update character locations
if (typeof Macro !== 'undefined' && Macro.get('updateCharacterLocations')) {
$.wiki('<<updateCharacterLocations>>');
}
Engine.play('fhBedroom');
});
} else {
$('#skip-start').addClass('disabled').css('cursor', 'not-allowed');
}
}
// Handle card clicks
$('.skip-card').on('click', function() {
var $card = $(this);
var section = $card.data('section');
var title = $card.data('title');
var effect = $card.data('effect');
var stat = $card.data('stat');
var value = $card.data('value');
// Update selection in same section
$card.closest('.skip-section').find('.skip-card').removeClass('selected');
$card.addClass('selected');
// Store selection
selections[section] = {
title: title,
effect: effect,
stat: stat,
value: value
};
// Update status
$('#' + section + '-status').text('✓ ' + title).addClass('selected');
// Check if all selected
updateStartButton();
});
// Restore existing selections
sections.forEach(function(section) {
if (selections[section]) {
var title = selections[section].title;
$('.skip-card[data-section="' + section + '"]').each(function() {
if ($(this).data('title') === title) {
$(this).addClass('selected');
$('#' + section + '-status').text('✓ ' + title).addClass('selected');
}
});
}
});
updateStartButton();
});
<</script>>
<<nobr>><<set $location = "skyline">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Skyline District">>The towering heart of the city's business district. Three massive skyscrapers pierce the clouds, their glass facades reflecting the urban landscape.<</narrative>>
<<showLocationChars "skyline">>
<<navMenu>>
<<navCard "towerA">>
<<navCard "towerB">>
<<navCard "towerC">>
<<navCard "downTown">>
<</navMenu>><div class="divnobr">
<<silently>>
<<set $location = "fhBedroom">>
<<set $isSleeping = true>>
<<set _sleepMaxMinutes = 540>>
/* 1. REQUESTED DURATION (picker; hard cap = 9h) */
<<set _requestedDuration = 480>>
<<if def $pickerMemory and def $pickerMemory.sleepDuration>>
<<set _requestedDuration = $pickerMemory.sleepDuration>>
<</if>>
<<if _requestedDuration > _sleepMaxMinutes>><<set _requestedDuration = _sleepMaxMinutes>><</if>>
/* 2. ENERGY ↔ TIME 20→max = 8h (480m); 0→max takes longer (linear in deficit vs span above 20) */
<<set _sleepEnergyFullMinutes = 480>>
<<set _energySleepFloor = 20>>
<<set _energyCurrent = parseInt($energy || 0)>>
<<set _energyMax = Math.max(1, $energyMax || 80)>>
<<set _energyToFill = Math.max(0, _energyMax - _energyCurrent)>>
<<if _energyMax > _energySleepFloor>>
<<set _energySpanAboveFloor = _energyMax - _energySleepFloor>>
<<else>>
<<set _energySpanAboveFloor = Math.max(1, _energyMax)>>
<</if>>
<<set _minutesToFillEnergy = Math.round((_energyToFill / _energySpanAboveFloor) * _sleepEnergyFullMinutes)>>
/* 3. ALARM CALCULATION */
<<set _currentDay = $timeSys.weekday>>
<<set _nextDay = (_currentDay + 1) % 7>>
<<set _isNextDayWeekend = (_nextDay == 0 || _nextDay == 6)>>
<<set _alarmActive = false>>
<<if _isNextDayWeekend && $alarm.weekendEnabled>>
<<set _alarmActive = true>>
<<set _alarmHour = $alarm.weekendHour>>
<<set _alarmMinute = $alarm.weekendMinute>>
<<elseif !_isNextDayWeekend && $alarm.weekdayEnabled>>
<<set _alarmActive = true>>
<<set _alarmHour = $alarm.weekdayHour>>
<<set _alarmMinute = $alarm.weekdayMinute>>
<</if>>
<<set _alarmWakeup = false>>
<<set _alarmSleepDuration = _requestedDuration>>
<<if _alarmActive>>
<<set _currentMinutes = ($timeSys.hour * 60) + $timeSys.minute>>
<<set _alarmMinutes = (_alarmHour * 60) + _alarmMinute>>
<<if _alarmMinutes <= _currentMinutes>>
<<set _alarmMinutes += 1440>>
<</if>>
<<set _timeUntilAlarm = _alarmMinutes - _currentMinutes>>
<<if _timeUntilAlarm > 0 && _timeUntilAlarm < _requestedDuration>>
<<set _alarmSleepDuration = _timeUntilAlarm>>
<<set _alarmWakeup = true>>
<</if>>
<</if>>
/* 4. DETERMINE FINAL DURATION + WAKE TYPE */
/* Priority: alarm first, then energy fill, then full requested duration */
<<if _alarmWakeup && _alarmSleepDuration <= _minutesToFillEnergy>>
/* Alarm rings before energy fills */
<<set _sleepDuration = _alarmSleepDuration>>
<<set _wakeType = "alarm">>
<<elseif _minutesToFillEnergy < _requestedDuration && !_alarmWakeup>>
/* Energy fills before requested duration ends */
<<set _sleepDuration = _minutesToFillEnergy>>
<<set _wakeType = "energy">>
<<elseif _alarmWakeup>>
/* Alarm rings (energy fills first or at same time) */
<<set _sleepDuration = _alarmSleepDuration>>
<<set _wakeType = "alarm">>
<<else>>
/* Slept the full requested amount */
<<set _sleepDuration = _requestedDuration>>
<<set _wakeType = "natural">>
<</if>>
/* Safety clamps (never more than 9h in one sleep) */
<<if _sleepDuration < 30>><<set _sleepDuration = 30>><</if>>
<<if _sleepDuration > _sleepMaxMinutes>><<set _sleepDuration = _sleepMaxMinutes>><</if>>
/* 5. RECOVERY energy scales with actual sleep vs time-to-full; stress/health: 9h = 100% ratio */
<<set _recoveryRatio = _sleepDuration / _sleepMaxMinutes>>
<<set _energyGain = 0>>
<<if _energyToFill > 0 && _minutesToFillEnergy > 0>>
<<set _energyGain = Math.min(Math.round(_sleepDuration * (_energyToFill / _minutesToFillEnergy)), _energyToFill)>>
<</if>>
<<set _stressLoss = Math.round(50 * _recoveryRatio)>>
<<set _healthGain = Math.round(20 * _recoveryRatio)>>
/* 6. APPLY CHANGES */
<<advanceTime _sleepDuration>>
<<set $isSleeping = false>>
<<set $appearance.hairCombed = 0>>
<<gainStat "energy" _energyGain>>
<<loseStat "stress" _stressLoss>>
<<gainStat "health" _healthGain>>
/* 7. NOTIFICATIONS */
<<set _sleepHours = Math.floor(_sleepDuration / 60)>>
<<set _sleepMins = _sleepDuration % 60>>
<<if _wakeType == "alarm">>
<<run window.notifySuccess("You wake up to your alarm.")>>
<<elseif _wakeType == "energy">>
<<run window.notifySuccess("You feel fully rested.")>>
<<else>>
<<run window.notifySuccess("You slept well.")>>
<</if>>
/* 8. SLEEP CONTEXT FOR WAKE-UP OPTIONS
Consumed by <<bedWakeUp>> to decide which continue-sleep buttons to show.
Follow-up passages (sleepToDesiredTime / sleepUntilAlarm / sleepSnoozeApply)
update .sleepDuration after extra sleep so flags recompute correctly. */
<<set $sleepWakeContext = {}>>
<<set $sleepWakeContext.wakeType = _wakeType>>
<<set $sleepWakeContext.sleepDuration = _sleepDuration>>
<<set $sleepWakeContext.requestedDuration = _requestedDuration>>
<<set $sleepWakeContext.alarmActive = _alarmActive>>
<<if _alarmActive>>
<<set $sleepWakeContext.timeUntilAlarm = _timeUntilAlarm>>
<<else>>
<<set $sleepWakeContext.timeUntilAlarm = 0>>
<</if>>
<<set _sceneIndex = random(1, 3)>>
<</silently>>
/* ==================== SLEEP NARRATIVE ==================== */
<<narrative "Sleep">>
You curl up under the covers, the softness of the bed inviting you to let go of the day's worries. You close your eyes, letting your breathing slow.
<</narrative>>
<<if _sceneIndex == 1>>
<<narrative>>
You drift off quickly, sinking into a dreamless, peaceful slumber...
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/bedroom/sleep/sleep1.mp4" "60%">>
<<elseif _sceneIndex == 2>>
<<narrative>>
The comfort of the bed wraps around you as you slowly fade into sleep...
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/bedroom/sleep/sleep2.mp4" "100%">>
<<elseif _sceneIndex == 3>>
<<narrative>>
Exhaustion takes over, and you find yourself asleep almost as soon as your head hits the pillow...
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/bedroom/sleep/sleep3.mp4" "60%">>
<</if>>
/* ==================== WAKE-UP NARRATIVE ==================== */
<<if _wakeType == "alarm">>
<<narrative>>
The shrill sound of your alarm cuts through your sleep. You jolt awake, groggy and blinking. You silence it and lie there for a moment, then force yourself upright.
<</narrative>>
<<elseif _wakeType == "energy">>
<<narrative>>
Your eyes open on their own no alarm, no noise. You just feel //done//. Rested. Clear-headed. Your body decided it had what it needed and simply woke up.
<</narrative>>
<<else>>
<<narrative>>
You wake up naturally, feeling completely refreshed. The room looks the same, but everything feels a little lighter.
<</narrative>>
<</if>>
<<flushNotifications>>
</div>
<<bedWakeUp>><<nobr>>
<<silently>>
<<set $location = "fhBedroom">>
<<if ndef $daily>><<set $daily = {}>><</if>>
/* Duration: use snooze picker memory first (avoid stale selectedDuration from other pickers) */
<<set _snooze = NaN>>
<<if def $pickerMemory and def $pickerMemory.snoozeDuration>>
<<set _snooze = parseInt($pickerMemory.snoozeDuration, 10)>>
<</if>>
<<if _snooze !== _snooze || _snooze < 1>>
<<set _snooze = parseInt($selectedDuration, 10)>>
<</if>>
<<if _snooze !== _snooze || _snooze < 1>><<set _snooze = 15>><</if>>
<<if _snooze > 60>><<set _snooze = 60>><</if>>
<<set $isSleeping = true>>
<<advanceTime _snooze>>
<<set $isSleeping = false>>
<<set _energyGain = Math.max(1, Math.round(_snooze * 0.25))>>
<<gainStat "energy" _energyGain>>
<<loseStat "stress" Math.round((_snooze / 60) * 10)>>
<<set $daily.snoozeCount = (parseInt($daily.snoozeCount, 10) || 0) + 1>>
<<if $sleepWakeContext>>
<<set _slept = parseInt($sleepWakeContext.sleepDuration, 10) || 0>>
<<set $sleepWakeContext.sleepDuration = _slept + _snooze>>
<</if>>
<<run window.notifySuccess("You snooze for a bit longer.")>>
<<flushNotifications>>
<<recalculateStats>>
<<run $(document).trigger(':passagerender')>>
<</silently>>
<<narrative "Snooze">>
You tap the alarm and roll over, slipping under again just for a little while. When the quiet pulls you back up you're not sure how long it's been, only that the covers //felt// too good to leave.
<</narrative>>
<<bedWakeUp>>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "fhBedroom">>
<<set _ctx = $sleepWakeContext>>
<<if ndef _ctx>><<set _ctx = {}>><</if>>
<<set _reqDur = parseInt(_ctx.requestedDuration, 10)>>
<<if _reqDur !== _reqDur>><<set _reqDur = 0>><</if>>
<<set _slept = parseInt(_ctx.sleepDuration, 10)>>
<<if _slept !== _slept>><<set _slept = 0>><</if>>
<<set _extra = Math.max(0, _reqDur - _slept)>>
<<if _extra > 0>>
<<set $isSleeping = true>>
<<advanceTime _extra>>
<<set $isSleeping = false>>
/* Scaled recovery same spirit as sleep.twee (9h = 100%) */
<<set _energyMaxNow = parseInt($energyMax || 80, 10)>>
<<set _energyToFill = Math.max(0, _energyMaxNow - parseInt($energy || 0, 10))>>
<<if _energyToFill > 0>>
<<gainStat "energy" Math.min(_energyToFill, Math.round(_extra * 0.2))>>
<</if>>
<<loseStat "stress" Math.round((50 * _extra) / 540)>>
<<gainStat "health" Math.round((20 * _extra) / 540)>>
<<set $sleepWakeContext.sleepDuration = _slept + _extra>>
<<set $sleepWakeContext.wakeType = "planned">>
<<run window.notifySuccess("You sleep a little longer.")>>
<<flushNotifications>>
<</if>>
<</silently>>
<<narrative "Back to Sleep">>
You sink back into the covers, letting yourself drift until the hour you originally set out for. When you stir again the room feels different quieter, softer and you know your body finally had the time it asked for.
<</narrative>>
<<bedWakeUp>>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "fhBedroom">>
<<set _ctx = $sleepWakeContext>>
<<if ndef _ctx>><<set _ctx = {}>><</if>>
<<set _tUntilAlarm = parseInt(_ctx.timeUntilAlarm, 10)>>
<<if _tUntilAlarm !== _tUntilAlarm>><<set _tUntilAlarm = 0>><</if>>
<<set _slept = parseInt(_ctx.sleepDuration, 10)>>
<<if _slept !== _slept>><<set _slept = 0>><</if>>
<<set _extra = Math.max(0, _tUntilAlarm - _slept)>>
<<if _extra > 0>>
<<set $isSleeping = true>>
<<advanceTime _extra>>
<<set $isSleeping = false>>
/* Recover energy from extra sleep until alarm (never 0 while still tired) */
<<set _energyCurrentNow = parseInt($energy || 0, 10)>>
<<set _energyMaxNow = Math.max(1, parseInt($energyMax || 80, 10))>>
<<set _energyToFill = Math.max(0, _energyMaxNow - _energyCurrentNow)>>
<<set _energySleepFloor = 20>>
<<set _sleepEnergyFullMinutes = 480>>
<<if _energyMaxNow > _energySleepFloor>>
<<set _energySpanAboveFloor = _energyMaxNow - _energySleepFloor>>
<<else>>
<<set _energySpanAboveFloor = Math.max(1, _energyMaxNow)>>
<</if>>
<<set _minutesToFillEnergy = Math.max(1, Math.round((_energyToFill / _energySpanAboveFloor) * _sleepEnergyFullMinutes))>>
<<if _energyToFill > 0>>
<<set _energyGain = Math.min(_energyToFill, Math.max(1, Math.round(_extra * (_energyToFill / _minutesToFillEnergy))))>>
<<gainStat "energy" _energyGain>>
<</if>>
<<loseStat "stress" Math.round((_extra / 60) * 8)>>
<<gainStat "health" 2>>
<<set $sleepWakeContext.sleepDuration = _slept + _extra>>
<<set $sleepWakeContext.wakeType = "alarm">>
<<run window.notifySuccess("You rest until the alarm.")>>
<<flushNotifications>>
<</if>>
<</silently>>
<<narrative "Almost Morning">>
You let the dark settle again, hovering in a thin fog of half-sleep. Sounds blur; time stretches. Then your alarm tears through the quiet sharp, insistent and you surface all at once, reaching for it in the dark.
<</narrative>>
<<bedWakeUp>>
<</nobr>><<set $location = "fhBedroom">>
<<narrative "Wait...">>
You start to get up, then stop.
You're not wearing anything. The hallway outside isn't exactly private there's a chance someone could be up.
(//Do you really want to walk out like this?//)
<</narrative>>
<div class="location-actions">
<<btn "Go Anyway" "fhUpperBath">><</btn>>
<<btn "Back to Bed" "fhBedroom">><</btn>>
</div><<nobr>>
<<silently>>
<<set $location = "fhBedroom">>
/* 1. TIME + ENERGY */
<<set _energyMax = $energyMax || 80>>
<<set _energyCurrent = parseInt($energy || 0)>>
<<set _energyThreshold = Math.round(_energyMax * 0.85)>>
<<set _currentHour = $timeSys.hour>>
<<set _isEvening = (_currentHour >= 20)>>
/* Energy block only applies before 20:00 */
<<set _tooEnergized = (!_isEvening && _energyCurrent >= _energyThreshold)>>
/* 2. SAVE CURRENT OUTFIT FOR POST-SLEEP RESTORATION */
<<if $wardrobe && $wardrobe.equipped>>
<<set $preSleepOutfit = clone($wardrobe.equipped)>>
<<else>>
<<set $preSleepOutfit = null>>
<</if>>
/* 3. CHECK SLOTS */
<<set _sleepwearEquipped = (
$wardrobe &&
$wardrobe.equipped &&
$wardrobe.equipped.sleepwear &&
$wardrobe.equipped.sleepwear !== ""
)>>
<<set _hasUnderwear = (
$wardrobe &&
$wardrobe.equipped &&
($wardrobe.equipped.bra || $wardrobe.equipped.panty)
)>>
<</silently>>
/* === TOO ENERGIZED TO SLEEP (only before 20:00) === */
<<if _tooEnergized>>
<<silently>><<run window.notifyWarning("Still too much energy to sleep.")>><</silently>>
<<narrative "Not Tired Yet">>
You start getting changed for bed, but halfway through you catch yourself in the mirror and pause.
It's only <<print $timeSys.hour + ":00">>, and your body is still buzzing. Your mind keeps drifting to things left undone places to be, people to see. You're just not tired enough to call it a night yet.
You toss your clothes back on and admit it: the day isn't over.
<</narrative>>
<div class="location-actions">
<<btn "Get Up" "fhBed">><</btn>>
</div>
/* === READY TO SLEEP === */
<<else>>
<<narrative "Getting Ready for Bed">>
You move through your room, winding down for the night.
<</narrative>>
<div class="location-actions">
/* --- Change into Pajamas (sleepwear slot bra/panty stay on underneath) --- */
<<if _sleepwearEquipped>>
<<btn "Change into Pajamas" "sleepGate_highArousal">>
<<set _sw = $wardrobe.equipped.sleepwear>>
<<set _bra = $wardrobe.equipped.bra>>
<<set _panty = $wardrobe.equipped.panty>>
<<set $wardrobe.equipped = {}>>
<<set $wardrobe.equipped.sleepwear = _sw>>
<<if _bra>><<set $wardrobe.equipped.bra = _bra>><</if>>
<<if _panty>><<set $wardrobe.equipped.panty = _panty>><</if>>
<<recalculateStats>>
<<updateClothesNotification>>
<</btn>>
<<else>>
<<btn "Change into Pajamas" "" "locked" "Select sleepwear from your wardrobe first.">><</btn>>
<</if>>
/* --- Sleep in Underwear (requires Corruption 3+ and underwear equipped) --- */
<<if $corruption >= 3 && _hasUnderwear>>
<<btn "Sleep in Underwear" "sleepGate_highArousal">>
<<set _bra = $wardrobe.equipped.bra>>
<<set _panty = $wardrobe.equipped.panty>>
<<set $wardrobe.equipped = {}>>
<<if _bra>><<set $wardrobe.equipped.bra = _bra>><</if>>
<<if _panty>><<set $wardrobe.equipped.panty = _panty>><</if>>
<<recalculateStats>>
<<updateClothesNotification>>
<</btn>>
<<elseif $corruption < 3>>
<<btn "Sleep in Underwear" "" "locked" "Requires Corruption 3.">><</btn>>
<<else>>
<<btn "Sleep in Underwear" "" "locked" "No underwear equipped.">><</btn>>
<</if>>
/* --- Sleep Naked (Corruption 5+) --- */
<<if $corruption >= 5>>
<<btn "Sleep Naked" "sleepGate_highArousal">>
<<set $wardrobe.equipped = {}>>
<<recalculateStats>>
<<updateClothesNotification>>
<</btn>>
<<else>>
<<btn "Sleep Naked" "" "locked" "Requires Corruption 5.">><</btn>>
<</if>>
</div>
<div class="location-actions">
<<btn "Back" "fhBed">><</btn>>
</div>
<</if>>
<</nobr>>
<<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<logDailyActivity "dinerWaitress2" "talk">>
<<set _char = $characters.dinerWaitress2>>
<<set _stats = $characters.dinerWaitress2.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase dishwasherOnBreak: worked >0h but <8h today; dishwasherDone: full 8h done within last 3h */
<<set _phase = "common">>
<<if $job && $job.id === "ruby_dishwasher" && def $jobState>>
<<if $jobState.hoursToday >= 8>>
<<if $jobState.lastShiftEndHour !== null && ($timeSys.hour - $jobState.lastShiftEndHour) <= 3>>
<<set _phase = "dishwasherDone">>
<</if>>
<<elseif $jobState.hoursToday > 0>>
<<set _phase = "dishwasherOnBreak">>
<</if>>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _isDinerBackArea = ($location === "dinerRubysKitchen" || $location === "dinerRubysStorage" || $location === "dinerRubysCorridor" || $location === "dinerRubysBathroom" || $location === "dinerRubysDressingRoom")>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.sofiaTopics && setup.sofiaTopics[_phase] && setup.sofiaTopics[_phase][_levelKey] && setup.sofiaTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.sofiaTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _ctxKey = "dinerRubys_" + _phase + "_" + _timeSlot>>
<<if setup.sofiaTopics && setup.sofiaTopics[_phase] && setup.sofiaTopics[_phase][_levelKey] && setup.sofiaTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.sofiaTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.sofiaTopics && setup.sofiaTopics.common && setup.sofiaTopics.common[_levelKey] && setup.sofiaTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.sofiaTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _fallbackKey = "dinerRubys_common_" + _timeSlot>>
<<if setup.sofiaTopics && setup.sofiaTopics.common && setup.sofiaTopics.common[_levelKey] && setup.sofiaTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.sofiaTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
/* Pick topic if pool exists */
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "Sofia ignores you, busy typing on her phone.", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "dinerWaitress2" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "dinerWaitress2" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "dinerWaitress2" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "dinerWaitress2" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerWaitress2.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>>/* ==========================================
SOFIA TALK - Diana Gossip (one-time)
Unlocks after emmaGossipDone.
Voice: narrative = external (you); inner voice = first-person self-talk.
========================================== */
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<<logDailyActivity "Talked to Sofia">>
<<gainCharacterStat "dinerWaitress2" "friendship" 2>>
<<advanceTime 15>>
<</silently>>
<<image "assets/content/scenes/interactions/sofia/level1/sofiaFrontAfternoon-1.webp" "100%">>
<<dialog "player">>
There was someone here yesterday. The woman with Vince.
<</dialog>>
<<dialog "dinerWaitress2">>
Diana? I saw her.
<</dialog>>
<<dialog "player">>
What did you think?
<</dialog>>
<<dialog "dinerWaitress2">>
(thinking)
Look that woman has a body and she knows how to use it. If I looked like that I'd do the same thing. Why wouldn't I?
<</dialog>>
<<narrative>>
She said it straight. No apology.
<</narrative>>
<<dialog "player">>
The guys in the kitchen were talking about her...
<</dialog>>
<<dialog "dinerWaitress2">>
(laughing)
Of course they were. They always talk like that. If you were someone they wanted like that, you'd want it too I know I would.
<</dialog>>
<<dialog "player">>
(quietly)
Maybe.
<</dialog>>
<<dialog "player">>
So just... give men what they want?
<</dialog>>
<<dialog "dinerWaitress2">>
If that's what they want, why not? Especially if you're getting something back. Vince gives her nice dinners, gifts, money she gives Vince what he wants. Nobody's fooling anybody. Everyone knows what they want, nobody's lying.
<</dialog>>
<<narrative>>
She shrugged.
<</narrative>>
<<dialog "dinerWaitress2">>
We're the ones who make it complicated.
<</dialog>>
<<dialog "player">>
Emma got really upset.
<</dialog>>
<<narrative>>
Sofia paused. For a moment it looked like she was going to say something.
<</narrative>>
<<dialog "dinerWaitress2">>
Emma has... her own reasons.
<</dialog>>
<<narrative>>
She moved on. Didn't go further.
<</narrative>>
<<dialog "player">>
What do you mean?
<</dialog>>
<<dialog "dinerWaitress2">>
(lightly)
Nothing. We were talking about Diana.
<</dialog>>
<<narrative>>
She paused as she turned away.
<</narrative>>
<<dialog "dinerWaitress2">>
One more thing... About Vince. Just be careful. He's the boss, but just be careful.
<</dialog>>
<<narrative>>
She didn't explain. Didn't continue.
<</narrative>>
<<narrative>>
Emma's "her own reasons" stuck with you as Sofia walked away.
<</narrative>>
<<dialog "player">>
(Inner voice)
She knows something she's not saying.
<</dialog>>
<<nobr>>
<<set $flags.sofiaGossipDone = true>>
<<completeObjective "something_different" "talkSofia">>
<</nobr>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerWaitress2.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div><<nobr>><<set $location = "sororityHouse">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Alpha Omega House">>A well-maintained sorority house. Sisters come and go for meetings and events.<</narrative>>
<<showLocationChars "sororityHouse">>
<<navMenu>><<navCard "universityDistrict">><</navMenu>><<nobr>><<set $location = "southside">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Southside">>The rougher part of town. Industrial buildings, working-class residents, and graffiti-covered walls.<</narrative>>
<<showLocationChars "southside">>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "apartmentComplex">>
<<navCard "cornerBlock">>
<<navCard "laundromat">>
<<navCard "oldFactory">>
<<navCard "shopPawn">>
<<navCard "storeLiquor">>
<<navCard "suburbs">>
<<navCard "redLightCenter">>
<</navMenu>><<nobr>><<set $location = "storeAdult">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Eros Boutique">>A shop selling adult toys, videos, and accessories. Open 24 hours.<</narrative>>
<<showLocationChars "storeAdult">>
<<navMenu>><<navCard "redLightCenter">><</navMenu>><<nobr>>
<<set $location = "storeBags">>
<<set _shopItems = [
"bag_ll_classic_tote_camel_t2",
"bag_ll_crossbody_chain_cherry_t2",
"bag_ll_bucket_suede_forest_t3",
"bag_ll_structured_top_handle_black_t3",
"bag_ll_quilted_shoulder_cream_t3",
"bag_ll_turn_lock_clutch_patent_red_t4",
"bag_ll_hobo_slouchy_taupe_t2",
"bag_ll_satchel_dome_navy_t3",
"bag_ll_backpack_mini_black_t2",
"bag_ll_minaudiere_crystal_silver_t4",
"bag_ll_camera_crossbody_mustard_t2",
"bag_ll_tote_canvas_leather_trim_t3",
"bag_ll_hobo_suede_tan_top_handle_t3",
"bag_ll_flap_chain_velvet_burgundy_t4",
"bag_ll_shopper_transparent_black_trim_t2",
"bag_ll_crossbody_saddle_tan_t3",
"bag_ll_top_handle_snake_print_t4",
"bag_ll_clutch_envelope_satin_navy_t3",
"bag_ll_slouchy_nylon_puffer_olive_t2",
"bag_ll_drawstring_leather_ivory_t3",
"bag_ll_top_handle_bamboo_woven_t3",
"bag_ll_clutch_acrylic_marbled_teal_t4",
"bag_ll_tote_perforated_leather_white_t3",
"bag_ll_backpack_convertible_olive_t3",
"bag_ll_clutch_foldover_croc_black_t4",
"bag_ll_shoulder_nylon_chain_black_t2",
"bag_ll_micro_chain_bag_silver_t4",
"bag_ll_crossbody_guitar_strap_tan_t3",
]>>
<<updateCharacterLocations>>
<<shop "Luxe Leather" "Designer Bag Store" _shopItems "floorSecond">>
<</nobr>><<nobr>>
<<set $location = "storeBook">>
<<set _shopItems = [
"book_quiet_pitch",
"book_never_back_down",
"book_power_of_words",
"book_just_talk",
"book_social_blueprint",
"book_mastering_connection",
"book_how_to_find_anything",
"book_source_and_truth",
"book_information_age",
"book_think_it_through",
"book_lateral_mind",
"book_mind_unlocked",
"book_level_up",
"book_pro_gamer_mindset",
"book_competitive_edge",
"book_from_scratch",
"book_kitchen_manual",
"book_mastery_kitchen",
"book_stillwater",
"book_letters_never_sent",
"book_curious_mind",
"mag_think_quick",
"mag_home_flavors",
"mag_social_edge",
"mag_green_corner",
"mag_creative_pulse",
"mag_inner_drive"
]>>
<<set _shopBack = "oldTown">>
<<updateCharacterLocations>>
<</nobr>>
<<narrative "Bookworm's Corner">>A used book store with hidden treasures. Every shelf holds a new adventure.<</narrative>>
<<showLocationChars "storeBook">>
<<shop "Bookworm's Corner" "Books & Magazines" _shopItems _shopBack>><<nobr>>
<<set $location = "storeClothingA">>
<<set _shopItems = [
"top_oversized_graphic_white",
"top_ribbed_henley_navy",
"top_bretton_stripe_navy_white",
"top_plaid_flannel_red_black",
"top_waffle_knit_olive",
"top_floral_chiffon_blush",
"top_ribbed_crop_burgundy",
"top_tie_front_lemon",
"top_pointelle_cream",
"top_off_shoulder_floral_blue",
"top_cowl_neck_satin_champagne",
"top_boxy_cropped_charcoal",
"top_slogan_hoodie_white",
"top_gingham_crop_rose",
"top_tie_dye_tee_pastel",
"top_tropical_blouse_cream",
"top_ribbed_coord_top_grey",
"top_vneck_basic_black",
"top_sweatshirt_mint",
"top_polo_knit_navy",
"top_halter_neck_pink",
"top_wrap_blouse_white",
"top_broderie_anglaise_ivory",
"top_graphic_tee_band_black",
"top_wrap_cropped_navy",
"top_camisole_lace_trim_black",
"top_sweater_chunky_cream",
"top_turtleneck_ribbed_camel",
"top_tank_ribbed_white",
"top_blouse_puff_sleeve_lilac",
"top_button_up_poplin_blue",
"top_cardigan_cropped_plum",
"top_tee_vneck_sage",
"top_off_shoulder_knit_rust",
"bottom_mom_jeans_blue",
"bottom_acid_wash_mini_skirt",
"bottom_floral_midi_skirt",
"bottom_cargo_pants_beige",
"bottom_pleated_tennis_skirt_white",
"bottom_ribbed_knit_maxi_grey",
"bottom_faux_leather_pants_black",
"bottom_denim_shorts_distressed",
"bottom_linen_wide_leg_navy",
"bottom_gingham_tiered_skirt",
"bottom_skinny_jeans_black",
"bottom_sweatpants_grey",
"bottom_corduroy_pants_brown",
"bottom_tiered_maxi_white",
"bottom_biker_shorts_black",
"bottom_wrap_skirt_floral",
"bottom_denim_skirt_midi",
"bottom_leather_mini_skirt",
"bottom_plaid_mini_skirt",
"bottom_lounge_shorts_grey",
"bottom_jeans_bootcut_blue",
"bottom_cargo_shorts_khaki",
"bottom_velour_sweatpants_pink",
"bottom_capri_pants_denim",
"bottom_wide_leg_sweatpants_black",
"bottom_pleated_midi_skirt_khaki",
"bottom_high_low_skirt_black",
"bottom_leggings_grey_marled",
"bottom_dolphin_shorts_blue",
"bottom_chino_shorts_beige",
"bottom_bell_bottom_jeans_washed",
"bottom_layered_ruffle_mini_pink",
"bottom_paperbag_pants_olive",
"bottom_jorts_denim",
"bottom_maxi_skirt_denim",
"bottom_track_shorts_navy",
"bottom_sweatshorts_grey",
"bottom_mom_jeans_black",
"bottom_wrap_skirt_linen",
"bottom_leggings_flared_black",
"dress_clothing_a_basic_cotton_sundress_t0",
"dress_clothing_a_floral_mini_dress_t1",
"dress_clothing_a_ribbed_knit_midi_t1",
"dress_clothing_a_denim_pinafore_t2",
"dress_clothing_a_wrap_polka_dot_t2",
"dress_clothing_a_basic_bodycon_mini_t3",
"dress_clothing_a_slogan_tshirt_dress_t1",
"dress_clothing_a_star_slip_dress_t3",
"dress_clothing_a_stripe_shirt_dress_t1",
"dress_clothing_a_acid_denim_zip_t2",
"dress_clothing_a_gingham_smock_t1",
"dress_clothing_a_tropical_halter_maxi_t2",
"dress_clothing_a_tie_dye_ribbed_tank_t1",
"dress_clothing_a_plaid_skater_t2",
"dress_clothing_a_faux_leather_pinafore_t3",
"dress_clothing_a_color_block_polo_t1",
"dress_clothing_a_floral_peasant_t2",
"dress_clothing_a_corduroy_dungaree_t2",
"dress_clothing_a_ribbed_turtleneck_mini_t1",
"dress_clothing_a_poplin_puff_sleeve_t2",
"dress_clothing_a_striped_linen_blend_t2",
"dress_clothing_a_smocked_midi_t2",
"dress_clothing_a_crochet_beach_t3",
"dress_clothing_a_metallic_slip_t3",
"coat_north_classic_denim",
"coat_north_black_puffer",
"coat_north_olive_parka",
"coat_north_navy_windbreaker",
"coat_north_red_flannel_shacket",
"coat_north_brown_corduroy",
"coat_north_grey_fleece",
"body_nl_basic_cotton_black_t1",
"body_nl_sport_zip_front_navy_t2",
"body_nl_ribbed_long_sleeve_grey_t2",
"body_nl_seamless_athletic_white_t2",
"sleep_nl_basic_cotton_set_pink_t1",
"sleep_nl_fleece_pants_grey_t2",
"sleep_nl_oversize_tee_white_t1",
"sleep_nl_flannel_plaid_red_t2",
"sleep_nl_jersey_nightgown_navy_t2",
"sleep_nl_thermal_henley_grey_t2",
"sleep_nl_sport_lounge_set_black_t2",
"bra_north_basic_cotton_white",
"bra_north_basic_cotton_nude",
"bra_north_sports_mesh_grey",
"bra_north_sports_mesh_navy",
"bra_north_bralette_rib_black",
"bra_north_bralette_rib_olive",
"bra_north_bralette_lace_ecru",
"bra_north_high_impact_sports_crop_black",
"bra_north_bralette_padded_dusty_rose",
"bra_north_bralette_cutout_sage",
"panty_north_basic_brief_white",
"panty_north_basic_brief_black",
"panty_north_heather_hipster_grey",
"panty_north_seamless_bikini_nude",
"panty_north_ribbed_cheeky_navy",
"panty_north_seamless_high_waist_white",
"panty_north_cotton_thong_olive",
"panty_north_micro_mesh_cheeky_black",
"panty_north_ribbed_high_waist_rose",
"panty_north_minimal_thong_sage",
"panty_north_lace_trim_hipster_white",
"panty_north_seamless_thong_navy",
"panty_north_high_waist_brief_black",
"socks_north_ankle_white",
"socks_north_ankle_black",
"socks_north_crew_sport_white",
"socks_north_knee_high_black",
"socks_north_thigh_high_ribbed_grey",
"socks_north_ankle_frill_pink",
"bikini_top_nl_basic_triangle_navy_t1",
"bikini_top_nl_sport_crop_black_t2",
"bikini_bot_nl_basic_brief_navy_t1",
"bikini_bot_nl_sport_boyshort_black_t2",
"swimsuit_nl_classic_red_t1",
"swimsuit_nl_athletic_racerback_black_t2",
]>>
<<updateCharacterLocations>>
<<shop "Northline Apparel" "Casual Clothing Store" _shopItems "floorGround">>
<</nobr>><<nobr>>
<<set $location = "storeClothingB">>
<<set _shopItems = [
"top_satin_cowl_emerald",
"top_ribbed_crop_terracotta",
"top_sheer_floral",
"top_asym_hem_crop_red",
"top_knot_front_camel",
"top_off_shoulder_ribbed_navy",
"top_cutout_back_white",
"top_leopard_print_crop",
"top_velvet_slip_purple",
"top_corsage_blazer_cream",
"top_zip_up_crop_red",
"top_pleated_silk_top_chamomile",
"top_bandage_crop_neon",
"top_silk_blouse_rose_gold",
"top_halter_neck_blazer_pink",
"top_mesh_flared_sleeve_black",
"top_corset_bustier_beige",
"top_wide_collar_knit",
"top_flutter_sleeve_top_mint",
"top_ribbed_turtleneck_pink",
"top_strappy_crop_cobalt",
"top_ruched_sides_camel",
"top_bow_detail_blouse_ivory",
"top_sequin_tube_black",
"top_directional_knit_greige",
"top_crown_neck_corset_silver_grey",
"top_floral_jacquard_terracotta",
"top_mesh_body_con_black",
"top_houndstooth_crop_white_black",
"bottom_faux_leather_pants_black_vera",
"bottom_satin_midi_skirt_green",
"bottom_tailored_shorts_white",
"bottom_split_hem_leggings_black",
"bottom_cargo_pants_beige_vera",
"bottom_ruched_mini_skirt_red",
"bottom_vegan_leather_shorts_brown",
"bottom_plisse_trousers_gold",
"bottom_wrap_skirt_black",
"bottom_ribbed_knit_skirt_midi",
"bottom_distressed_straight_jeans_wash",
"bottom_sequined_mini_skirt_silver",
"bottom_latex_leggings_red",
"bottom_asymmetric_midi_skirt_black",
"bottom_cutout_trousers_white",
"bottom_vinyl_mini_skirt_black",
"bottom_jacquard_shorts_gold",
"bottom_zipper_skirt_leather",
"bottom_sheer_lace_pants_black",
"bottom_satin_culottes_navy",
"bottom_corset_waist_jeans",
"bottom_fringe_mini_skirt_silver",
"bottom_slit_joggers_grey",
"bottom_velvet_leggings_burgundy",
"bottom_cutout_mini_skirt_red",
"bottom_ruched_pencil_skirt",
"bottom_flare_leggings_zebra",
"bottom_wrap_maxi_skirt_silk",
"bottom_sequined_shorts_black",
"bottom_lace_up_pants_leather",
"bottom_high_slit_skirt_white",
"bottom_pleated_skirt_leather",
"bottom_mesh_leggings_black",
"bottom_cargo_pants_satin",
"bottom_micro_skirt_tartan",
"dress_clothing_b_satin_slip_midi_t2",
"dress_clothing_b_asym_hem_cutout_t3",
"dress_clothing_b_leopard_wrap_midi_t3",
"dress_clothing_b_ruched_bodycon_t3",
"dress_clothing_b_corduroy_shirt_dress_t2",
"dress_clothing_b_abstract_print_midi_t3",
"dress_clothing_b_mono_knit_long_sleeve_t2",
"dress_clothing_b_faux_leather_mini_t4",
"dress_clothing_b_paillette_open_back_t4",
"dress_clothing_b_stripe_knit_mini_t3",
"dress_clothing_b_tiered_flounce_t2",
"dress_clothing_b_velvet_bodice_slip_t3",
"dress_clothing_b_crochet_cover_up_t3",
"dress_clothing_b_off_shoulder_floral_t3",
"dress_clothing_b_high_neck_bodycon_t4",
"dress_clothing_b_tulle_skirt_combo_t3",
"dress_clothing_b_shimmer_knit_tube_t4",
"dress_clothing_b_lace_insert_midi_t3",
"coat_vera_camel_trench",
"coat_vera_black_blazer",
"coat_vera_navy_wool_oversized",
"coat_vera_white_boucle",
"coat_vera_houndstooth_overcoat",
"body_vm_satin_wrap_emerald_t3",
"body_vm_cut_out_knit_tan_t3",
"body_vm_jersey_plunge_black_t3",
"body_vm_leopard_mesh_sleeve_t4",
"sleep_vm_satin_cami_shorts_pink_t3",
"sleep_vm_knit_wrap_robe_cream_t2",
"sleep_vm_slip_dress_floral_navy_t3",
"sleep_vm_lounge_pants_wide_leg_ribbed_olive_t2",
"sleep_vm_tee_boy_shorts_grey_stripe_t2",
"sleep_vm_satin_button_down_black_piping_t3",
"sleep_vm_slip_dress_leopard_t4",
"sleep_vm_velour_sweatsuit_plum_t3",
"sleep_vm_lace_trim_ribbed_tank_olive_t2",
"sleep_vm_boyfriend_cardigan_oatmeal_t2",
"sleep_vm_satin_kimono_robe_blush_t3",
]>>
<<updateCharacterLocations>>
<<shop "VERA Mode" "Fashion Boutique" _shopItems "floorSecond">>
<</nobr>><<nobr>>
<<set $location = "storeClothingC">>
<<set _shopItems = [
"top_structured_blazer_charcoal",
"top_crepe_silk_navy_high",
"top_tailored_tuxedo_white",
"top_scuba_rollneck_black",
"top_asym_satin_blouse_midnight",
"top_pearl_button_tailored_wine",
"top_silhouette_satin_wrap",
"top_knit_crew_fine_gold",
"top_leather_trim_black",
"top_drape_boatneck_ecru",
"top_wool_blend_oatmeal",
"top_structured_crepe_peplum_red",
"top_satin_amethyst",
"top_jersey_italian_blue",
"top_cashmere_blend_cream",
"top_geometric_print_blouse_sage",
"top_silk_jersey_wrap_coral_red",
"top_formal_button_blouse_blush",
"top_silk_halter_deep_crimson",
"bottom_tailored_trousers_black",
"bottom_pleated_midi_skirt_camel",
"bottom_silk_slip_skirt_champagne",
"bottom_wide_leg_pants_white",
"bottom_cashmere_knit_skirt_grey",
"bottom_leather_pencil_skirt_burgundy",
"bottom_asymmetric_skirt_navy",
"bottom_boucle_mini_skirt_cream",
"bottom_tweed_trousers_grey",
"bottom_satin_wrap_skirt_olive",
"bottom_velvet_maxi_skirt_black",
"bottom_high_waist_flare_pants_camel",
"bottom_sequin_maxi_skirt_silver",
"bottom_cashmere_sweatpants_beige",
"bottom_wool_skirt_suit_navy",
"bottom_silk_cargo_pants_black",
"bottom_pleated_culottes_cream",
"bottom_pencil_skirt_lace_black",
"bottom_high_waist_shorts_linen",
"bottom_tweed_mini_skirt_pink",
"bottom_wrap_trousers_olive",
"bottom_crocheted_maxi_skirt_white",
"bottom_leather_leggings_black",
"dress_clothing_c_crepe_midi_t3",
"dress_clothing_c_jacquard_sheath_t3",
"dress_clothing_c_lace_bodycon_t3",
"dress_clothing_c_tailored_pencil_t3",
"dress_clothing_c_satin_evening_midi_t4",
"dress_clothing_c_fine_stripe_shirt_t3",
"dress_clothing_c_velvet_midi_t4",
"dress_clothing_c_one_shoulder_long_t4",
"dress_clothing_c_tweed_boucle_mini_t3",
"coat_fifth_black_leather_moto",
"coat_fifth_vintage_bomber",
"coat_fifth_sherpa_denim",
"coat_fifth_matrix_vinyl",
"coat_fifth_red_racing",
"sleep_fc_silk_long_pajama_navy_white_t3",
"sleep_fc_mulberry_silk_slip_champagne_t4",
"sleep_fc_cashmere_lounge_set_grey_t4",
"sleep_fc_silk_wrap_robe_black_t4",
]>>
<<updateCharacterLocations>>
<<shop "Fifth Avenue" "Luxury Boutique" _shopItems "floorSecond">>
<</nobr>><<nobr>>
<<set $location = "storeCorner">>
<<set _shopItems = ["energy_drink", "coffee", "sandwich", "water_bottle", "chocolate_bar", "apple", "mag_small_talk", "mag_makeup_studio"]>>
<<set _shopBack = "maplewood">>
<<updateCharacterLocations>>
<<narrative "Quick Mart">><</narrative>>
<<showLocationChars "storeCorner">>
<<questPrompts>>
<</nobr>>
<<shop "Quick Mart" "Convenience Store" _shopItems _shopBack>><<nobr>>
<<set $location = "storeCosmetics">>
<<set _shopItems = [
"makeup_kit",
"portable_makeup",
"comb",
"hair_cream",
"face_cream"
]>>
<<updateCharacterLocations>>
<<showLocationChars "storeCosmetics">>
<<shop "Lumière Beauty" "Makeup & Personal Care" _shopItems "floorGround">>
<</nobr>><<nobr>>
<<set $location = "storeElectronics">>
<<set _shopItems = [
"webcam",
"laptop"
]>>
<<updateCharacterLocations>>
<<shop "Tech World" "Electronics Store" _shopItems "floorGround">>
<</nobr>><<nobr>><<set $location = "storeHardware">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Dan's Hardware">>A well-stocked hardware store. Tools, supplies, and helpful advice from the owner.<</narrative>>
<<showLocationChars "storeHardware">>
<<navMenu>><<navCard "oldTown">><</navMenu>><<nobr>>
<<set $location = "storeJewelry">>
<<set _shopItems = [
"jewel_nc_mini_initial_silver_t2",
"jewel_nc_pearl_drop_gold_t3",
"jewel_nc_chunky_layered_rose_gold_t3",
"jewel_nc_crystal_tennis_choker_t4",
"jewel_nc_moon_star_gold_t2",
"jewel_nc_emerald_cut_zirconia_silver_t4",
"jewel_nc_snake_chain_gold_t3",
"jewel_nc_halo_heart_silver_t3",
"jewel_nc_pendant_sapphire_gold_t4",
"jewel_nc_locket_rose_gold_t2",
"jewel_nc_toggle_bar_silver_t3",
"jewel_nc_drop_y_necklace_crystal_t4",
"jewel_er_studs_crystal_simple_t2",
"jewel_er_hoops_gold_chunky_t3",
"jewel_er_drop_pearl_gold_t4",
"jewel_er_threader_silver_star_t2",
"jewel_er_cuff_ear_climber_gold_t3",
"jewel_er_huggie_hoop_pave_silver_t2",
"jewel_er_tassel_fringe_rose_gold_t3",
"jewel_er_sapphire_halo_studs_silver_t4",
"jewel_er_irregular_resin_tortoise_t2",
"jewel_br_tennis_crystal_silver_t4",
"jewel_br_chunky_chain_gold_t3",
"jewel_br_bangle_set_stacked_silver_t2",
"jewel_br_cuff_minimal_rose_gold_t3",
"jewel_br_charm_flora_silver_t2",
"jewel_br_pearl_stretch_cream_t2",
"jewel_br_paperclip_link_gold_t3",
"jewel_br_mesh_crystal_knot_silver_t4",
"jewel_br_lever_hinge_diamond_gold_t4",
"jewel_br_ledger_id_tag_silver_t2",
"jewel_br_macrame_pull_cord_rose_t2",
"jewel_rg_solitaire_princess_crystal_t4",
"jewel_rg_stacking_bands_gold_t2",
"jewel_rg_signet_engraved_silver_t3",
"jewel_rg_cocktail_morganite_rose_t4",
"jewel_rg_eternity_band_crystal_silver_t3",
"jewel_rg_bypass_pearl_crystal_gold_t3",
"jewel_rg_mini_chain_link_silver_t2",
"jewel_rg_sapphire_trillion_gold_t4",
"jewel_rg_hammered_dome_rose_t3"
]>>
<<updateCharacterLocations>>
<<shop "Diamond Dreams" "Premium Jewelry Store" _shopItems "floorSecond">>
<</nobr>><<nobr>>
<<set $location = "storeLingerieA">>
<<set _shopItems = [
"bra_sl_basic_cotton_blush_bralette",
"bra_sl_satin_bandeau_ivory",
"bra_sl_lace_balconette_dusty_mauve",
"bra_sl_satin_plunge_navy",
"bra_sl_ribbon_satin_blush_set",
"bra_sl_lace_bralette_cream_floral",
"bra_sl_push_up_satin_black",
"bra_sl_plunge_emerald_lace_strappy",
"bra_sl_sheer_mesh_burgundy_balconette",
"bra_sl_premium_satin_rose_gold_plunge",
"bra_sl_cotton_soft_cup_white",
"bra_sl_lace_bralette_lavender",
"bra_sl_satin_balconette_coral_set",
"bra_sl_satin_plunge_forest_green",
"bra_sl_sheer_mesh_peach_balconette",
"bra_sl_premium_lace_plunge_wine_red",
"bra_sl_premium_sheer_embroidered_charcoal",
"bra_sl_premium_satin_strapless_nude",
"bra_sl_premium_lace_halter_teal",
"panty_sl_ribbon_satin_blush_set",
"panty_sl_satin_coral_set",
"panty_sl_satin_cheeky_ivory",
"panty_sl_lace_brief_dusty_mauve",
"panty_sl_satin_thong_navy",
"panty_sl_lace_thong_cream_floral",
"panty_sl_satin_bikini_black",
"panty_sl_strappy_thong_emerald",
"panty_sl_sheer_mesh_cheeky_burgundy",
"panty_sl_rose_gold_satin_thong",
"panty_sl_cotton_brief_white",
"panty_sl_lace_thong_lavender",
"panty_sl_satin_high_waist_forest_green",
"panty_sl_sheer_mesh_thong_peach",
"panty_sl_premium_lace_thong_wine_red",
"panty_sl_premium_embroidered_cheeky_charcoal",
"garter_sl_basic_black_sheer",
"garter_sl_satin_red_bow",
"garter_sl_lace_white_wide",
"garter_sl_ribbon_satin_blush_set",
"garter_sl_vintage_rose_print_wide",
"garter_sl_strappy_emerald_lace",
"garter_sl_corset_lace_up_black",
"garter_sl_suspender_brief_navy",
"garter_sl_premium_rose_gold_chain",
"sleep_sl_babydoll_mesh_pink_t3",
"sleep_sl_chemise_lace_trim_navy_t3",
"sleep_sl_robe_sheer_lace_white_t4",
"sleep_sl_teddy_silk_lace_red_t3",
"sleep_sl_satin_pj_short_set_emerald_t3",
"sleep_sl_playsuit_satin_champagne_t3",
"socks_sl_fishnet_ankle_black",
"socks_sl_sheer_ankle_nude",
"socks_sl_sheer_knee_high_black",
"socks_sl_lace_top_knee_high_white",
"socks_sl_stay_up_sheer_nude",
"socks_sl_stay_up_lace_black",
"socks_sl_stocking_seamed_black",
"socks_sl_stocking_plain_nude",
"socks_sl_stocking_fishnet_red",
"socks_sl_pantyhose_sheer_black",
"socks_sl_pantyhose_opaque_black",
"socks_sl_pantyhose_fishnet_nude",
]>>
<<updateCharacterLocations>>
<<shop "Silk & Lace" "Lingerie & Intimates" _shopItems "floorSecond">>
<</nobr>><<nobr>>
<<set $location = "storeLingerieB">>
<<set _shopItems = [
"bra_is_lace_balconette_purple_t3",
"bra_is_sheer_mesh_plunge_red_t3",
"bra_is_strappy_cage_black_mesh_t4",
"bra_is_sheer_lace_plunge_night_blue_t4",
"bra_is_harness_strap_black_satin_t4",
"bra_is_snake_print_mesh_plunge_t4",
"bra_is_barely_there_triangle_red_t5",
"bra_is_open_cup_lace_black_t5",
"bra_is_premium_wet_look_plunge_black_t4",
"bra_is_premium_sheer_lace_red_harness_t5",
"bra_is_cut_out_mesh_lavender_t4",
"bra_is_body_chain_gold_black_t5",
"bra_is_premium_leather_look_red_t5",
"bra_is_premium_floral_embroidered_ecru_t3",
"bra_is_premium_crystal_chain_black_t5",
"panty_is_lace_thong_purple_t3",
"panty_is_sheer_mesh_cheeky_red_t3",
"panty_is_strappy_cage_black_mesh_t4",
"panty_is_sheer_lace_thong_night_blue_t4",
"panty_is_harness_strap_black_satin_t4",
"panty_is_snake_print_mesh_thong_t4",
"panty_is_barely_there_micro_red_t5",
"panty_is_open_crotch_lace_black_t5",
"panty_is_wet_look_thong_black_t4",
"panty_is_sheer_lace_red_harness_t5",
"panty_is_crotchless_body_chain_gold_black_t5",
"body_is_lace_plunge_black_t3",
"body_is_strappy_harness_red_t4",
"body_is_sheer_fishnet_black_t4",
"body_is_vinyl_zip_front_black_t4",
"body_is_crotchless_mesh_lace_t5",
"body_is_cut_out_oring_emerald_t4",
"body_is_mini_suspender_black_t4",
"body_is_velvet_burnout_red_t3",
"body_is_pearl_detail_thong_t5",
"body_is_nude_illusion_tattoo_t4",
"body_is_silk_wrap_champagne_t3",
"body_is_corset_boning_white_lace_t4",
"body_is_gunmetal_chainmail_t5",
"body_is_turtleneck_fishnet_neon_pink_t4",
"body_is_underwire_balcony_leopard_t4",
"garter_is_classic_black_lace",
"garter_is_red_strappy_harness",
"garter_is_silk_white_pearl",
"garter_is_leather_buckle",
"garter_is_pink_mesh_ruffle",
"garter_is_navy_velvet",
"garter_is_burgundy_satin",
"garter_is_gold_chain_drop",
"garter_is_leopard_print_mesh",
"garter_is_emerald_lace",
"garter_is_hot_pink_vinyl",
"garter_is_white_fishnet_elastic",
"sleep_is_robe_fishnet_black_t4",
"sleep_is_chemise_latex_zip_black_t5",
"sleep_is_babydoll_sheer_lace_red_t5",
"sleep_is_lace_crop_shorts_crotchless_black_t5",
"sleep_is_micro_robe_silk_pink_t4",
"socks_is_stocking_lace_top_red_t3",
"socks_is_stocking_seamed_red_t3",
"socks_is_pantyhose_corset_lacing_t4",
"socks_is_stay_up_fishnet_bow_t4",
"socks_is_stocking_strappy_garter_t5",
"socks_is_pantyhose_wet_look_black_t5",
"socks_is_pantyhose_floral_lace_t5",
"socks_is_pantyhose_crotchless_fishnet_t5",
"socks_is_micromesh_suspender_tights_t5",
]>>
<<updateCharacterLocations>>
<<shop "Intimate Secrets" "Premium Lingerie & Bodysuits" _shopItems "floorSecond">>
<</nobr>><<nobr>><<set $location = "storeLiquor">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Quick Liquor">>A corner liquor store with bars on the windows. Open late into the night.<</narrative>>
<<showLocationChars "storeLiquor">>
<<navMenu>><<navCard "southside">><</navMenu>><<nobr>>
<<set $location = "storeShoeA">>
<<set _shopItems = [
"shoes_su_chunky_platform_sneaker_white_t1",
"shoes_su_canvas_flat_floral_t1",
"shoes_su_leopard_loafer_t2",
"shoes_su_color_block_sandal_t0",
"shoes_su_chelsea_boot_black_t2",
"shoes_su_espadrille_wedge_tan_t2",
"shoes_su_slip_on_sneaker_grey_t0",
"shoes_su_strappy_flat_sandal_t1",
"shoes_su_ankle_boot_suede_t2",
"shoes_su_canvas_high_top_black_t1",
"shoes_su_rubber_rain_boot_yellow_t2",
"shoes_su_gladiator_flat_sandal_tan_t1",
"shoes_su_fuzzy_house_slipper_pink_t0",
"shoes_su_slip_on_mule_black_t2",
"shoes_su_wedge_sneaker_white_t1",
"shoes_su_mary_jane_patent_black_t2",
"shoes_su_combat_boot_black_t2",
"shoes_su_cork_slide_sandal_brown_t0",
"shoes_su_kitten_heel_pump_nude_t2",
"shoes_su_slouchy_knee_boot_taupe_t2",
"shoes_su_platform_oxford_burgundy_t2",
"shoes_su_retro_sneaker_pastel_t1",
"shoes_su_clear_jelly_sandal_pink_t0",
"shoes_su_hiker_boot_tan_t2",
"shoes_su_woven_mule_brown_t2",
"shoes_su_court_sneaker_white_t1",
"shoes_su_block_ankle_boot_snake_t3",
]>>
<<updateCharacterLocations>>
<<shop "StepUp Footwear" "Casual Shoe Store" _shopItems "floorGround">>
<</nobr>><<nobr>>
<<set $location = "storeShoeB">>
<<set _shopItems = [
"shoes_ss_classic_pump_patent_black_t3",
"shoes_ss_ankle_strap_block_sandal_nude_t2",
"shoes_ss_clear_pvc_mule_lucite_t4",
"shoes_ss_extreme_platform_red_t5",
"shoes_ss_architectural_gold_t4",
"shoes_ss_gladiator_thigh_high_black_t4",
"shoes_ss_crystal_bow_pump_silver_t3",
"shoes_ss_leopard_bootie_t3",
"shoes_ss_slouchy_over_knee_boot_black_t4",
"shoes_ss_peep_toe_platform_nude_t4",
"shoes_ss_kitten_slingback_white_t2",
"shoes_ss_strappy_wrap_neon_pink_t4",
"shoes_ss_mirror_chrome_pump_silver_t4",
"shoes_ss_snakeskin_stiletto_top_green_t3",
"shoes_ss_minimalist_two_strap_black_t3",
"shoes_ss_stripper_pole_clear_extreme_t5",
"shoes_ss_caged_lace_up_heel_black_t4",
"shoes_ss_pointed_slingback_red_t3",
"shoes_ss_t_strap_pump_black_white_t3",
"shoes_ss_studded_ankle_boot_black_t4",
"shoes_ss_feather_mule_pink_t3",
"shoes_ss_velvet_platform_pump_navy_t4",
"shoes_ss_corset_bootie_lace_white_t4",
"shoes_ss_thigh_high_stiletto_vinyl_red_t5",
]>>
<<updateCharacterLocations>>
<<shop "Stiletto Studio" "High-End Footwear" _shopItems "floorSecond">>
<</nobr>><<nobr>>
<<set $location = "storeSports">>
<<set _shopItems = [
"yoga_mat",
"top_sports_mesh_tank_seafoam",
"top_sports_zip_hoodie_cobalt",
"top_sports_crop_bralet",
"top_sports_long_sleeve_compression",
"top_sports_tank_color_block_orange",
"top_sports_zip_track_jacket",
"top_sports_crop_hoodie_grey",
"top_sports_bra_crop_rose",
"top_sports_windbreaker_neon",
"top_sports_mesh_back_yoga",
"top_sports_fleece_pullover_purple",
"bottom_yoga_pants_black",
"bottom_gym_shorts_grey",
"bottom_running_tights_blue",
"bottom_sweatpants_oversized_grey",
"bottom_biker_shorts_black_sports",
"bottom_track_pants_navy",
"bottom_seamless_leggings_pink",
"bottom_tennis_skirt_white",
"bottom_scrunch_shorts_peach",
"bottom_cargo_joggers_khaki",
"bottom_wrap_athletic_skirt_black",
"bottom_sweatshorts_blue",
"bottom_yoga_flare_pants_grey",
"bottom_mesh_panel_leggings_black",
"bottom_retro_running_shorts_red",
"coat_sports_black_windrunner",
"coat_sports_grey_track_jacket",
"coat_sports_neon_running",
"coat_sports_white_hoodie",
"coat_sports_navy_sideline",
"shoes_fb_pro_running_sneaker_neon_t2",
"shoes_fb_crossfit_trainer_grey_pink_t1",
"shoes_fb_basketball_high_top_red_white_t2",
"shoes_fb_recovery_slide_black_t0",
"shoes_fb_barefoot_trail_runner_olive_t2",
"shoes_fb_tennis_court_shoe_white_navy_t1",
"shoes_fb_slip_on_athletic_knit_grey_t1",
"shoes_fb_water_shoe_teal_t0",
"shoes_fb_cushion_jogger_purple_t2",
"shoes_fb_boxing_high_top_black_gold_t2",
"shoes_fb_lifting_velcro_white_black_t2",
"shoes_fb_athletic_slide_orange_black_t1",
"shoes_fb_minimalist_gym_barefoot_black_t2",
"shoes_fb_retro_90s_runner_silver_blue_t3",
"shoes_fb_cleated_turf_neon_yellow_t3",
"shoes_fb_winter_sport_fur_boot_black_t2",
"shoes_fb_skate_canvas_black_white_t1",
"bra_fb_sports_low_impact_white",
"bra_fb_sports_medium_black",
"bra_fb_sports_medium_navy",
"bra_fb_sports_high_crop_charcoal",
"bra_fb_sports_strap_detail_olive",
"bra_fb_sports_mesh_panel_coral",
"bra_fb_sports_bralette_tie_dye",
]>>
<<updateCharacterLocations>>
<<shop "FastBreak Athletics" "Sports Store" _shopItems "floorGround">>
<</nobr>><<nobr>>
<<set $location = "storeSwim">>
<<set _shopItems = [
"bikini_top_al_triangle_solid_coral_t1",
"bikini_top_al_stripe_navy_racer_t1",
"bikini_top_al_tropical_bandeau_t1",
"bikini_top_al_geometric_halter_t2",
"bikini_top_al_floral_underwire_t2",
"bikini_top_al_color_block_tie_front_t2",
"bikini_top_al_ring_halter_solid_black_t3",
"bikini_top_al_underwire_neon_yellow_t3",
"bikini_top_al_lace_up_front_leopard_t3",
"bikini_top_al_micro_halter_coral_t4",
"bikini_top_al_wrap_around_white_t4",
"bikini_top_al_zip_front_scuba_black_t4",
"bikini_top_al_cut_out_asymmetric_red_t4",
"bikini_top_al_chain_strap_gold_t4",
"bikini_bot_al_classic_tie_coral_t1",
"bikini_bot_al_stripe_navy_boy_t1",
"bikini_bot_al_tropical_ruched_t1",
"bikini_bot_al_geometric_high_cut_t2",
"bikini_bot_al_floral_cheeky_t2",
"bikini_bot_al_color_block_hipster_t2",
"bikini_bot_al_ring_string_black_t3",
"bikini_bot_al_mesh_panel_neon_yellow_t3",
"bikini_bot_al_lace_up_side_leopard_t3",
"bikini_bot_al_micro_thong_coral_t4",
"bikini_bot_al_wrap_tie_white_t4",
"bikini_bot_al_zip_scuba_black_t4",
"bikini_bot_al_cut_out_asymmetric_red_t4",
"bikini_bot_al_chain_side_gold_t4",
"swimsuit_al_racerback_sporty_navy_t1",
"swimsuit_al_deep_plunge_tropical_t2",
"swimsuit_al_mesh_panel_black_neon_t2",
"swimsuit_al_zip_scuba_color_block_t3",
"swimsuit_al_asymmetric_cut_out_red_t3",
"swimsuit_al_belted_ribbed_white_t2",
"swimsuit_al_lace_up_sides_leopard_t3",
"swimsuit_al_high_cut_80s_neon_teal_t3",
"swimsuit_al_illusion_sheer_mesh_black_t4",
"swimsuit_al_glamour_chain_gold_backless_t4",
]>>
<<updateCharacterLocations>>
<<shop "Aqua Lane" "Bikini & Swimwear" _shopItems "floorGround">>
<</nobr>><<nobr>><<set $location = "streetFifth">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Fifth Avenue">>The most exclusive shopping street in the city. Designer boutiques and high-end services.<</narrative>>
<<showLocationChars "streetFifth">>
<<navMenu>>
<<navCard "barWine">>
<<navCard "boutiqueA">>
<<navCard "boutiqueB">>
<<navCard "boutiqueC">>
<<navCard "restaurantLuxury">>
<<navCard "salonBeautyFifth">>
<<navCard "hillcrest">>
<</navMenu>><<nobr>><<set $location = "studentBar">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Tap Room">>A popular dive bar with cheap drinks. Packed on weekends with college students.<</narrative>>
<<showLocationChars "studentBar">>
<<navMenu>><<navCard "universityDistrict">><</navMenu>><<nobr>><<set $location = "suburbs">><</nobr>>
<<updateCharacterLocations>>
<<narrative "Suburbs">>The outskirts of town. Cheaper housing but farther from everything. Some areas are run down.<</narrative>>
<<showLocationChars "suburbs">>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "apartmentsSuburbs">>
<<navCard "houseCrack">>
<<navCard "motelSuburbs">>
<<navCard "thePit">>
<<navCard "gangTerritory">>
<<navCard "southside">>
<</navMenu>><<nobr>>
<<set $location = "sunsetPark">>
<<updateCharacterLocations>>
<<narrative "Sunset Park">>
A beautiful park with walking paths, benches, and a small pond. Perfect for an afternoon stroll or watching the sunset.
<</narrative>>
<<showLocationChars "sunsetPark">>
<<questPrompts>>
<div class="location-actions">
<<set $activityOrigin = "sunsetPark">>
<<btnPicker "Rest" "parkRest" "mediumActivity">>
<<btnPicker "Sit on Bench" "parkBench" "mediumActivity">>
<<btn "Walk" "parkWalk" "default" 13>><</btn>>
<<activityButton "Go for a Jog" "parkJog" "outfit:sporty 3|minStat:energy 25" $daily.jogDone>>
<<activityButton "Do Yoga" "parkYoga" "outfit:sporty 3|item:yoga_mat|minStat:energy 25" $daily.yogaDone>>
</div>
<<navMenu>>
<<navCard "sunsetWC">>
<<navCard "maplewood">>
<</navMenu>>
<</nobr>><<nobr>>
<<set $location = "sunsetWC">>
<<set $returnAfterToilet = "sunsetWC">>
<<updateCharacterLocations>>
<<narrative "Park Restroom">>
A small public restroom by the park. Basic but clean - sink, mirror, and stalls.
<</narrative>>
<div class="location-actions">
<<bathroomActions>>
<<mirrorBtn>>
<<btn "Cabin" "parkWCCabin">><</btn>>
</div>
<<navMenu>>
<<navCard "sunsetPark">>
<</navMenu>>
<</nobr>>
/* ==========================================
SUGARCUBE WIDGETS - Stats Calculations
========================================== */
<<widget "updateBodyMeasurements">>
/* BUST SIZE MAPPING (Text -> CM) */
<<set _bustMap = {
"A": 80, "B": 85, "C": 90, "D": 95,
"DD": 100, "DD+": 105, "E": 110, "F": 115, "G": 120, "H": 130,
"Small": 80, "Average": 90, "Large": 100, "Huge": 120, "Massive": 140
}>>
<<if $player.bustSize && _bustMap[$player.bustSize]>>
<<set $body.bust = _bustMap[$player.bustSize]>>
<</if>>
/* HIP SIZE MAPPING (Text -> CM) */
<<set _hipMap = {
"Narrow": 85, "Average": 95, "Wide": 105, "Curvy": 115, "Huge": 130, "Massive": 150,
"Slim": 85, "Medium": 95, "Wide": 105, "Curvy": 115
}>>
<<if $player.hipSize && _hipMap[$player.hipSize]>>
<<set $body.hips = _hipMap[$player.hipSize]>>
<</if>>
/* Ensure other stats exist */
<<if !$body.height>><<set $body.height = 170>><</if>>
<<if !$body.weight>><<set $body.weight = 60>><</if>>
<<if !$body.muscleMass>><<set $body.muscleMass = 35>><</if>>
<<if !$body.bodyFat>><<set $body.bodyFat = 24>><</if>>
/* WAIST - auto from hips (ratio ~0.77 so Slim 85 → waist ~65–66) */
<<if !$body.waist || $player.hipSize>>
<<set $body.waist = Math.round($body.hips * 0.77)>>
<</if>>
/* Update Body Type String (Sync with Settings logic) */
/* If Bust is DD(100)+ or Hips Wide(105)+ -> Curvy */
<<if $body.bust >= 100 || $body.hips >= 105>>
<<set $body.bodyType = "Curvy">>
<<else>>
<<set $body.bodyType = "Normal">>
<</if>>
<</widget>>
<<widget "calculateBodyAppeal">>
<<set _bmi = $body.weight / Math.pow($body.height / 100, 2)>>
/* Core strength improves effective waist (tighter midsection) for WHR/symmetry – max ~12 cm at core 100 */
<<set _coreBonus = ($core || 0) * 0.12>>
<<set _effectiveWaist = Math.max(55, $body.waist - _coreBonus)>>
<<set _whr = _effectiveWaist / $body.hips>>
<<set _idealBMI = 21.5>>
<<set _bmiDiff = Math.abs(_bmi - _idealBMI)>>
<<set _bmiScore = Math.max(0, 100 - (_bmiDiff * 10))>>
<<set _idealWHR = 0.7>>
<<set _whrDiff = Math.abs(_whr - _idealWHR)>>
<<set _whrScore = Math.max(0, 100 - (_whrDiff * 200))>>
<<set _bustHipAvg = ($body.bust + $body.hips) / 2>>
<<set _symmetryRatio = _bustHipAvg / _effectiveWaist>>
<<set _idealSymmetry = 1.35>>
<<set _symDiff = Math.abs(_symmetryRatio - _idealSymmetry)>>
<<set _symmetryScore = Math.max(0, 100 - (_symDiff * 50))>>
<<set _idealMuscle = 40>>
<<set _idealFat = 20>>
<<set _muscleDiff = Math.abs($body.muscleMass - _idealMuscle)>>
<<set _fatDiff = Math.abs($body.bodyFat - _idealFat)>>
<<set _compScore = (Math.max(0, 100 - (_muscleDiff * 2)) * 0.5) + (Math.max(0, 100 - (_fatDiff * 2)) * 0.5)>>
/* Body appeal 0–50 scale: even best start choices keep looks in 30–35 range */
<<set $body.appeal = Math.round((( _bmiScore * 0.3) + (_whrScore * 0.3) + (_symmetryScore * 0.2) + (_compScore * 0.2)) * 0.5)>>
<</widget>>
<<widget "calculateFitness">>
<<set $fitness = Math.round(($upperBody + $core + $lowerBody + $cardio) / 4)>>
<</widget>>
<<widget "calculateLooks">>
/* Redirect to central stat calculation (StatCalculator) */
<<if def "recalculateStats">>
<<recalculateStats>>
<<else>>
/* Fallback: beauty = fitness + body + face + hair + dental; looks = beauty + hygiene + clothing; penalty when face+dental bad */
<<calculateFitness>>
<<calculateBodyAppeal>>
<<set _bodyS = ($body.appeal || 0) * 2>>
<<set _beauty = Math.min(100, ($fitness || 0) * 0.28 + _bodyS * 0.25 + ($appearance.faceCare || 0) * 0.20 + ($appearance.hairCare || 0) * 0.15 + ($appearance.dentalCare || 0) * 0.12)>>
<<set _careSum = ($appearance.faceCare || 0) + ($appearance.dentalCare || 0)>>
<<if _careSum < 25>>
<<set _beauty = _beauty * 0.12>>
<<elseif _careSum < 50>>
<<set _beauty = _beauty * 0.40>>
<</if>>
<<set $beauty = Math.round(Math.max(0, Math.min(100, _beauty)))>>
<<set _styleBase = (($makeup && $makeup.style >= 1 && $makeup.style <= 5) ? ($makeup.style * 20) : 0)>>
<<set _muState = ($makeup && $makeup.state) ? $makeup.state : "off">>
<<set _muQuality = ($makeup && ($makeup.quality !== undefined)) ? $makeup.quality : 0>>
<<if _muState === "off" || !$makeup || !$makeup.style>>
<<set _makeupScore = 0>>
<<elseif _muQuality >= 50 && _muState === "fresh">>
<<set _makeupScore = _styleBase>>
<<else>>
<<set _makeupScore = Math.round(_styleBase * (_muQuality / 100))>>
<</if>>
<<set _makeupScore = Math.max(0, Math.min(100, _makeupScore))>>
<<set _fallbackHygieneLooks = $gameSettings.hygieneRequirement ? ($hygiene || 0) : 100>>
<<set $looks = Math.round(_beauty * 0.50 + _fallbackHygieneLooks * 0.10 + ($clothingScore || 0) * 0.20 + _makeupScore * 0.20)>>
<</if>>
<</widget>>
<<widget "updateMakeupHourlyDecay">>
<<if !$gameSettings.makeupWearOff>><<return>><</if>>
<<if ndef $makeup>>
<<set $makeup = { state: "off", style: 0, quality: 0, lastDecayHourKey: "", lastAppliedHourKey: "", lastDecayMinuteStamp: 0 }>>
<<return>>
<</if>>
<<if ndef $makeup.lastDecayHourKey>><<set $makeup.lastDecayHourKey = "">><</if>>
<<if ndef $makeup.lastAppliedHourKey>><<set $makeup.lastAppliedHourKey = "">><</if>>
<<if ndef $makeup.lastDecayMinuteStamp>><<set $makeup.lastDecayMinuteStamp = 0>><</if>>
<<if $makeup.state === "off" || ($makeup.style || 0) < 1>>
<<set $makeup.lastDecayHourKey = "">>
<<set $makeup.lastDecayMinuteStamp = 0>>
<<return>>
<</if>>
<<set _currHourKey = $timeSys.year + "-" + String($timeSys.month).padStart(2, "0") + "-" + String($timeSys.day).padStart(2, "0") + "-" + String($timeSys.hour).padStart(2, "0")>>
<<run State.temporary._currMinuteStamp = Math.floor(Date.UTC($timeSys.year, $timeSys.month - 1, $timeSys.day, $timeSys.hour, $timeSys.minute || 0, 0) / 60000)>>
<<if !$makeup.lastDecayHourKey || $makeup.lastDecayHourKey === "" || ($makeup.lastDecayMinuteStamp || 0) <= 0>>
<<set $makeup.lastDecayHourKey = _currHourKey>>
<<set $makeup.lastDecayMinuteStamp = State.temporary._currMinuteStamp>>
<<return>>
<</if>>
<<run
(function () {
const S = State.variables;
const T = State.temporary;
const prevMinute = parseInt(S.makeup.lastDecayMinuteStamp || 0, 10);
const currMinute = parseInt(T._currMinuteStamp || 0, 10);
if (!Number.isFinite(prevMinute) || !Number.isFinite(currMinute) || prevMinute <= 0 || currMinute <= 0) {
T._elapsedMakeupHours = 0;
T._resetDecayAnchor = true;
return;
}
const elapsedMinutes = currMinute - prevMinute;
T._elapsedMakeupHours = Math.max(0, Math.floor(elapsedMinutes / 60));
T._resetDecayAnchor = elapsedMinutes < 0;
if (T._elapsedMakeupHours > 0) {
T._newMakeupDecayMinuteStamp = prevMinute + (T._elapsedMakeupHours * 60);
} else {
T._newMakeupDecayMinuteStamp = prevMinute;
}
})();
>>
<<set _elapsedMakeupHours = State.temporary._elapsedMakeupHours || 0>>
<<if _elapsedMakeupHours <= 0 || State.temporary._resetDecayAnchor>>
<<set $makeup.lastDecayHourKey = _currHourKey>>
<<set $makeup.lastDecayMinuteStamp = State.temporary._currMinuteStamp>>
<<return>>
<</if>>
<<set _makeupLoss = _elapsedMakeupHours * 12>>
<<set $makeup.quality = Math.max(0, ($makeup.quality || 0) - _makeupLoss)>>
<<if $makeup.quality <= 0>>
<<set $makeup.state = "off">>
<<set $makeup.style = 0>>
<<set $makeup.quality = 0>>
<<set $makeup.lastDecayHourKey = "">>
<<set $makeup.lastAppliedHourKey = "">>
<<set $makeup.lastDecayMinuteStamp = 0>>
<<else>>
<<set $makeup.state = (($makeup.quality || 0) < 50) ? "smeared" : "fresh">>
<<set $makeup.lastDecayHourKey = _currHourKey>>
<<set $makeup.lastDecayMinuteStamp = State.temporary._newMakeupDecayMinuteStamp || State.temporary._currMinuteStamp>>
<</if>>
<</widget>>
<<widget "dailyAppearanceUpdate">>
/* Hair Growth (approx 0.04cm/day = 1.2cm/month) */
<<if $gameSettings.hairGrowth>>
<<set $appearance.hairLengthCm += 0.04>>
<</if>>
/* Hair Messiness */
<<if $gameSettings.hairMessiness>>
<<set $appearance.hairMessiness = Math.min(100, $appearance.hairMessiness + 15)>>
<<set $appearance.hairCombed = 0>>
<</if>>
/* Body Hair Growth */
<<if $gameSettings.bodyHairGrowth>>
<<set $appearance.bodyHair.legs = Math.min(100, $appearance.bodyHair.legs + 5)>>
<<set $appearance.bodyHair.pubic = Math.min(100, $appearance.bodyHair.pubic + 5)>>
<<set $appearance.bodyHair.armpits = Math.min(100, $appearance.bodyHair.armpits + 5)>>
<</if>>
/* Appearance Care Decay: hair/face/dental care drops daily; skip if hair cream used today */
/* Note: Use ?? not || because 0 is valid - (0 || 100) would wrongly treat 0 as "use 100" */
<<if $gameSettings.hairCareDecay>>
<<if !$daily.hairCreamUsed>>
<<set $appearance.hairCare = Math.max(0, ($appearance.hairCare ?? 100) - 4)>>
<</if>>
<</if>>
/* Face care decay: skip if face cream used today */
<<if $gameSettings.faceCareDecay>>
<<if !$daily.faceCreamUsed>>
<<set $appearance.faceCare = Math.max(0, ($appearance.faceCare ?? 100) - 4)>>
<</if>>
<</if>>
/* Dental care decay: skip if dental care (toothpaste) used today */
<<if $gameSettings.dentalCareDecay>>
<<if !$daily.dentalCareUsed>>
<<set $appearance.dentalCare = Math.max(0, ($appearance.dentalCare ?? 100) - 4)>>
<</if>>
<</if>>
<</widget>>
<<widget "weeklyBodyUpdate">>
/* Body Degradation (Muscle atrophy if unused) */
<<if $gameSettings.bodyDegradation>>
<<set $body.muscleMass = Math.max(20, $body.muscleMass - 0.5)>>
<</if>>
<</widget>>
<<widget "calculateEnergyMax">>
<<calculateFitness>>
<<set $energyMax = 100 + Math.floor($fitness / 4)>>
<</widget>>
<<widget "discover">><<nobr>>
<<set _flag = "discovered" + $args[0]>>
<<set _name = $args[1]>>
<<if !State.variables[_flag]>>
<<set State.variables[_flag] = true>>
<<run window.notifySuccess("New Location Discovered: " + _name)>>
<</if>>
<</nobr>><</widget>>
<<widget "talkBackButton">><<nobr>>
<<set _currentCharLocation = $args[0]>>
<<set _preferMainPassageBack = !!($gameSettings && $gameSettings.talkBackToMainPassage)>>
<<if _preferMainPassageBack>>
<<set _mainBackTarget = $interactionEntryPassage || $location || previous()>>
<<btn "Back" _mainBackTarget "secondary">><<unset $interactionEntryPassage>><</btn>>
<<else>>
<<btn "Back" "CharacterInteraction" "secondary">><</btn>>
<</if>>
<</nobr>><</widget>>
<<nobr>><<set $location = "thePit">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Pit">>An underground fighting ring. Cash bets and brutal matches.<</narrative>>
<<showLocationChars "thePit">>
<<navMenu>><<navCard "suburbs">><</navMenu>><<nobr>><<set $location = "theatreMovie">><</nobr>>
<<updateCharacterLocations>>
<<narrative "CineLux Cinemas">>A multiplex cinema with the latest releases. The smell of popcorn is irresistible.<</narrative>>
<<showLocationChars "theatreMovie">>
<<navMenu>><<navCard "floorThird">><</navMenu>><<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<logDailyActivity "dinerClerk" "talk">>
<<set _char = $characters.dinerClerk>>
<<set _stats = $characters.dinerClerk.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase dishwasherOnBreak: worked >0h but <8h today; dishwasherDone: full 8h done within last 3h */
<<set _phase = "common">>
<<if $job && $job.id === "ruby_dishwasher" && def $jobState>>
<<if $jobState.hoursToday >= 8>>
<<if $jobState.lastShiftEndHour !== null && ($timeSys.hour - $jobState.lastShiftEndHour) <= 3>>
<<set _phase = "dishwasherDone">>
<</if>>
<<elseif $jobState.hoursToday > 0>>
<<set _phase = "dishwasherOnBreak">>
<</if>>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _isDinerBackArea = ($location === "dinerRubysKitchen" || $location === "dinerRubysStorage" || $location === "dinerRubysCorridor" || $location === "dinerRubysBathroom" || $location === "dinerRubysDressingRoom")>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.tomTopics && setup.tomTopics[_phase] && setup.tomTopics[_phase][_levelKey] && setup.tomTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.tomTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _ctxKey = "dinerRubys_" + _phase + "_" + _timeSlot>>
<<if setup.tomTopics && setup.tomTopics[_phase] && setup.tomTopics[_phase][_levelKey] && setup.tomTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.tomTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.tomTopics && setup.tomTopics.common && setup.tomTopics.common[_levelKey] && setup.tomTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.tomTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _fallbackKey = "dinerRubys_common_" + _timeSlot>>
<<if setup.tomTopics && setup.tomTopics.common && setup.tomTopics.common[_levelKey] && setup.tomTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.tomTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
/* Pick topic if pool exists */
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "Tom smiles and gives you a polite nod before returning to the cash register.", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "dinerClerk" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "dinerClerk" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "dinerClerk" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "dinerClerk" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerClerk.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<nobr>>
<<set $location = "towerA">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "Apex Tower">>
A modern office complex with a recreation center on the lower floors. Business professionals come and go.
<</narrative>>
<<showLocationChars "towerA">>
<<navMenu>>
<<navCard "gym">>
<<navCard "skyline">>
<</navMenu>><<nobr>>
<<set $location = "towerB">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "Sterling Tower">>
A sleek corporate tower housing various businesses and offices. The lobby is modern and minimalist.
<</narrative>>
<<showLocationChars "towerB">>
<<navMenu>>
<<navCard "bank">>
<<navCard "skyline">>
<</navMenu>><<nobr>>
<<set $location = "towerC">>
<</nobr>>
<<updateCharacterLocations>>
<<narrative "Crown Plaza">>
The most exclusive tower in downtown, featuring a rooftop lounge, jazz club, and luxury penthouse suites.
<</narrative>>
<<showLocationChars "towerC">>
<<navMenu>>
<<navCard "hotel">>
<<navCard "jazzClub">>
<<navCard "pethouseSuite">>
<<navCard "restaurantRoof">>
<<navCard "roofTopLounge">>
<<navCard "skyline">>
<</navMenu>><<nobr>>
<<silently>>
<<set $location = "townHall">>
<<updateCharacterLocations>>
<</silently>>
<<narrative "Town Hall">>
The original city administration building. Historic architecture and local government offices.
<</narrative>>
<<showLocationChars "townhall">>
<<questPrompts>>
<</nobr>>
<<navMenu>><<navCard "civicCenter">><</navMenu>><<nobr>><<set $location = "universityDistrict">><</nobr>>
<<updateCharacterLocations>>
<<narrative "University District">>The college area with young energy. Students, cafes, and academic buildings everywhere.<</narrative>>
<<showLocationChars "universityDistrict">>
<<hubAmbientActions>>
<<navMenu>>
<<navCard "campus">>
<<navCard "cafeteriaUni">>
<<navCard "dormsUni">>
<<navCard "fratHouse">>
<<navCard "lectureHall">>
<<navCard "libraryUni">>
<<navCard "sororityHouse">>
<<navCard "studentBar">>
<<navCard "hillcrest">>
<<navCard "oldTown">>
<</navMenu>><<silently>>
<<if ndef $returnAfterToilet>>
<<set $returnAfterToilet = "fhUpperBath">>
<</if>>
<<set $location = $returnAfterToilet>>
<<advanceTime 20 "relax">>
<<gainStat "hygiene" 100>>
<<set $appearance.hairCombed = 0>>
<<run window.notifySuccessIfStatsEnabled('hygiene', "Hygiene Restored")>>
<<gainStat "energy" 20>>
<<gainStat "mood" 5>>
<<gainStat "health" 5>>
<<loseStat "stress" 10>>
<<flushNotifications>>
<<recalculateStats>>
<<set _sceneIndex = random(1, 3)>>
<<set $afterShowerReturnLoc = $returnAfterToilet>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set _fitnessAvg = ($fitness || Math.round((($cardio || 0) + ($lowerBody || 0) + ($core || 0) + ($upperBody || 0)) / 4))>>
<<set _tier1Eligible = (_fitnessAvg >= 12) && (($confidence || 0) >= 20)>>
<<set _tier2Eligible = (($lowerBody || 0) >= 18) || (($core || 0) >= 16)>>
<<set _tier3Eligible = (_fitnessAvg >= 28) && (($confidence || 0) >= 45)>>
<<set _mirrorMoment = "">>
<<set _isFirstTierMoment = (
(_tier3Eligible && !$flags.bodyAwarenessTier3) ||
(_tier2Eligible && !$flags.bodyAwarenessTier2) ||
(_tier1Eligible && !$flags.bodyAwarenessTier1)
)>>
<<if ($afterShowerMirrorLastDayKey || 0) !== _dateKey && (_isFirstTierMoment || random(1, 100) <= 35)>>
<<if _tier3Eligible && !$flags.bodyAwarenessTier3>>
<<set _mirrorMoment = "afterShowerMirror_first_tier3">>
<<elseif _tier2Eligible && !$flags.bodyAwarenessTier2>>
<<set _mirrorMoment = "afterShowerMirror_first_tier2">>
<<elseif _tier1Eligible && !$flags.bodyAwarenessTier1>>
<<set _mirrorMoment = "afterShowerMirror_first_tier1">>
<<elseif $flags.bodyAwarenessTier1 || $flags.bodyAwarenessTier2 || $flags.bodyAwarenessTier3>>
<<set _mirrorMoment = "afterShowerMirror_ambient">>
<</if>>
<</if>>
<</silently>>
<<narrative "Shower">>
You step into the bathroom and lock the door behind you. Turning the faucet, you wait a moment for the water to warm up before stripping off your clothes.
Stepping under the spray, you let the hot water run over your body, relaxing your muscles. You lather up the soap and scrub away the grime, feeling completely refreshed.
<</narrative>>
<<if _sceneIndex == 1>>
<<narrative "Shower">>
The steam rises as the heat sinks into your body...
<</narrative>>
<<vid "assets/content/scenes/global/shower/shower1.mp4" "60%">>
<<elseif _sceneIndex == 2>>
<<narrative>>
The hot water relaxes your muscles...
<</narrative>>
<<vid "assets/content/scenes/global/shower/shower2.mp4" "60%">>
<<elseif _sceneIndex == 3>>
<<narrative>>
The heat clings to your skin, slow and comforting...
<</narrative>>
<<vid "assets/content/scenes/global/shower/shower3.mp4" "100%">>
<</if>>
<<if _mirrorMoment>>
<<include _mirrorMoment>>
<</if>>
<div class="location-actions">
<<btn "Finish & Dry Off" $returnAfterToilet>><</btn>>
</div>
<<nobr>>
<<silently>>
<<if ndef $returnAfterToilet>>
<<set $returnAfterToilet = "fhUpperBath">>
<</if>>
<<set $location = $returnAfterToilet>>
<<loseStat "bladder" 100>>
<<run window.notifySuccessIfStatsEnabled('bladder', "Bladder Emptied")>>
<<loseStat "hygiene" 5>>
<<advanceTime 5>>
<<flushNotifications>>
<</silently>>
<<if $contentPreferences.watersports>>
/* EXPLICIT MODE (Watersports ON) */
<<narrative "Toilet">>
You sit down on the toilet and relax your muscles...
<</narrative>>
<<vid "assets/content/scenes/global/bathroom/usetoilet/pee2.mp4" "60%">>
<<else>>
/* CENSORED MODE (Watersports OFF) - Safe Video */
<<narrative "Toilet">>
You use the toilet and feel much relieved. You wash your hands afterwards.
<</narrative>>
<<vid "assets/content/scenes/global/bathroom/usetoilet/pee1.mp4" "60%">>
<</if>>
<<narrative>>
You step over to the sink and wash your hands thoroughly with soap and water.
<</narrative>>
<<vid "assets/content/scenes/global/bathroom/washinghands.mp4" "60%">>
<div class="location-actions">
<<btn "Finish" $returnAfterToilet>><</btn>>
</div>
<</nobr>><<silently>>
<<if !$location && $interactionEntryPassage>>
<<set $location = $interactionEntryPassage>>
<</if>>
<<logDailyActivity "dinerManager" "talk">>
<<set _char = $characters.dinerManager>>
<<set _stats = $characters.dinerManager.stats>>
<<set _level = _stats.friendshipLevel || 1>>
/* Determine phase dishwasherOnBreak: worked >0h but <8h today; dishwasherDone: full 8h done within last 3h */
<<set _phase = "common">>
<<if $job && $job.id === "ruby_dishwasher" && def $jobState>>
<<if $jobState.hoursToday >= 8>>
<<if $jobState.lastShiftEndHour !== null && ($timeSys.hour - $jobState.lastShiftEndHour) <= 3>>
<<set _phase = "dishwasherDone">>
<</if>>
<<elseif $jobState.hoursToday > 0>>
<<set _phase = "dishwasherOnBreak">>
<</if>>
<</if>>
/* Determine time slot */
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour < 17>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _isDinerBackArea = ($location === "dinerRubysKitchen" || $location === "dinerRubysStorage" || $location === "dinerRubysCorridor" || $location === "dinerRubysBathroom" || $location === "dinerRubysDressingRoom")>>
<<set _levelKey = "level" + _level>>
<<set _topicPool = null>>
<<set _ctxKey = $location + "_" + _phase + "_" + _timeSlot>>
<<if setup.vinceTopics && setup.vinceTopics[_phase] && setup.vinceTopics[_phase][_levelKey] && setup.vinceTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.vinceTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _ctxKey = "dinerRubys_" + _phase + "_" + _timeSlot>>
<<if setup.vinceTopics && setup.vinceTopics[_phase] && setup.vinceTopics[_phase][_levelKey] && setup.vinceTopics[_phase][_levelKey][_ctxKey]>>
<<set _topicPool = setup.vinceTopics[_phase][_levelKey][_ctxKey]>>
<</if>>
<</if>>
<<if !_topicPool>>
<<set _fallbackKey = $location + "_common_" + _timeSlot>>
<<if setup.vinceTopics && setup.vinceTopics.common && setup.vinceTopics.common[_levelKey] && setup.vinceTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.vinceTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
<<if !_topicPool && _isDinerBackArea>>
<<set _fallbackKey = "dinerRubys_common_" + _timeSlot>>
<<if setup.vinceTopics && setup.vinceTopics.common && setup.vinceTopics.common[_levelKey] && setup.vinceTopics.common[_levelKey][_fallbackKey]>>
<<set _topicPool = setup.vinceTopics.common[_levelKey][_fallbackKey]>>
<</if>>
<</if>>
/* Pick topic if pool exists */
<<if _topicPool>>
<<set _topicKeys = Object.keys(_topicPool)>>
<<set _randomKey = _topicKeys[Math.floor(Math.random() * _topicKeys.length)]>>
<<set _topicArray = _topicPool[_randomKey]>>
<<set _topic = _topicArray[Math.floor(Math.random() * _topicArray.length)]>>
<<else>>
<<set _topic = { text: "Vince points at the clock, indicating he's too busy to chat.", friendship: 0, trust: 0, love: 0, lust: 0 }>>
<</if>>
/* Time and base stats */
<<advanceTime 15>>
<<loseStat "energy" 5>>
<<gainStat "mood" 2>>
/* Apply topic stat gains */
<<if _topic.friendship>><<gainCharacterStat "dinerManager" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "dinerManager" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "dinerManager" "love" _topic.love>><</if>>
<<if _topic.lust>><<gainCharacterStat "dinerManager" "lust" _topic.lust>><</if>>
<<recalculateStats>>
<</silently>>
<<nobr>>
<div class="talk-interaction">
<<print _topic.text>>
<div class="location-actions">
<<set _currentCharLocation = $characters.dinerManager.currentLocation>>
<<talkBackButton _currentCharLocation>>
</div>
</div>
<</nobr>><<nobr>><<set $location = "wallGraffiti">><</nobr>>
<<updateCharacterLocations>>
<<narrative "The Wall">>A concrete wall covered in colorful street art. Rival gangs mark their territory here.<</narrative>>
<<showLocationChars "wallGraffiti">>
<<navMenu>><<navCard "gangTerritory">><</navMenu>>/* bags DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.bags = [
{"id": "bag_ll_classic_tote_camel_t2", "name": "Classic Camel Tote", "brand": "Luxe Leather", "desc": "A timeless large pebbled leather tote bag in a rich camel color. Features long shoulder straps, gold-tone minimalist hardware, and plenty of space for daily essentials.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLClassicToteCamelT2.webp", "store": "storeBags", "slot": "bags", "silhouette": "tote", "baseLooks": 6, "price": 405, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "casual"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_crossbody_chain_cherry_t2", "name": "Cherry Chain Crossbody", "brand": "Luxe Leather", "desc": "Compact flap crossbody bag in deep cherry red smooth leather. Features a long gold chain strap and a magnetic snap closure.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLCrossbodyChainCherryT2.webp", "store": "storeBags", "slot": "bags", "silhouette": "crossbody", "baseLooks": 6, "price": 380, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_bucket_suede_forest_t3", "name": "Forest Suede Bucket Bag", "brand": "Luxe Leather", "desc": "A trendy bucket bag crafted from soft forest green suede. Features a leather drawstring closure and a wide crossbody strap.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLBucketSuedeForestT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "bucket", "baseLooks": 7, "price": 405, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "elegant"], "npcAppeal": {"professional": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_structured_top_handle_black_t3", "name": "Structured Top-Handle Briefcase", "brand": "Luxe Leather", "desc": "Fierce professional top-handle bag in rigid black Saffiano leather. Features a silver push-lock closure and sharp geometric lines.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLStructuredTopHandleBlackT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "top-handle", "baseLooks": 7, "price": 475, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {"elite": 4, "professional": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_quilted_shoulder_cream_t3", "name": "Quilted Cream Shoulder Bag", "brand": "Luxe Leather", "desc": "Elegant rectangular shoulder bag in soft quilted cream leather. A sliding gold chain strap allows it to be worn short or long.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLQuiltedShoulderCreamT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "shoulder", "baseLooks": 8, "price": 460, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 75, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {"elite": 3}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_turn_lock_clutch_patent_red_t4", "name": "Patent Red Envelope Clutch", "brand": "Luxe Leather", "desc": "A striking evening clutch in high-gloss patent red leather. Designed like a classic envelope with a gold-tone turn-lock detail.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLTurnLockClutchPatentRedT4.webp", "store": "storeBags", "slot": "bags", "silhouette": "clutch", "baseLooks": 9, "price": 530, "quality": "Luxury", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {"elite": 4}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_hobo_slouchy_taupe_t2", "name": "Slouchy Taupe Hobo Bag", "brand": "Luxe Leather", "desc": "An unstructured slouchy hobo bag in soft taupe pebble leather. Comfortable, roomy, and effortlessly casual for weekend errands.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLHoboSlouchyTaupeT2.webp", "store": "storeBags", "slot": "bags", "silhouette": "hobo", "baseLooks": 6, "price": 380, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_satchel_dome_navy_t3", "name": "Navy Dome Satchel", "brand": "Luxe Leather", "desc": "A classic semi-structured dome satchel in deep navy blue. Polished gold feet and zip-top closure. Perfect for the office.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLSatchelDomeNavyT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "satchel", "baseLooks": 7, "price": 460, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {"professional": 4}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_backpack_mini_black_t2", "name": "Black Leather Mini Backpack", "brand": "Luxe Leather", "desc": "A stylish and practical mini backpack in smooth black leather with silver zipper details. Keeps hands free without compromising on style.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLBackpackMiniBlackT2.webp", "store": "storeBags", "slot": "bags", "silhouette": "mini-backpack", "baseLooks": 7, "price": 415, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 75, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_minaudiere_crystal_silver_t4", "name": "Silver Crystal Minaudière", "brand": "Luxe Leather", "desc": "A tiny, jaw-dropping evening minaudière entirely encrusted with sparkling clear and silver crystals in a geometric pattern. Secured with an intricate box clasp.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLMinaudiereCrystalSilverT4.webp", "store": "storeBags", "slot": "bags", "silhouette": "minaudiere", "baseLooks": 10, "price": 470, "quality": "Premium", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_camera_crossbody_mustard_t2", "name": "Mustard Camera Bag", "brand": "Luxe Leather", "desc": "A boxy zip-around camera bag in vibrant mustard yellow pebble leather. Features a thick embossed woven strap for comfortable crossbody wear.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLCameraCrossbodyMustardT2.webp", "store": "storeBags", "slot": "bags", "silhouette": "camera-bag", "baseLooks": 6, "price": 405, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_tote_canvas_leather_trim_t3", "name": "Canvas & Leather Trim Tote", "brand": "Luxe Leather", "desc": "An elegant structured summer tote. Made of durable beige woven canvas and heavily trimmed with dark brown saddle leather on the handles and base.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLToteCanvasLeatherTrimT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "tote", "baseLooks": 7, "price": 460, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "casual", "professional"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_hobo_suede_tan_top_handle_t3", "name": "Tan Suede Top-Handle Hobo", "brand": "Luxe Leather", "desc": "A relaxed saddle-tan suede hobo resting lightly on the arm. Beautifully curved bottom edge with a sturdy single rolled top handle and matching tassels.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLHoboSuedeTanTopHandleT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "hobo", "baseLooks": 7, "price": 415, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_flap_chain_velvet_burgundy_t4", "name": "Burgundy Velvet Flap Bag", "brand": "Luxe Leather", "desc": "Luxurious evening flap bag crafted from deep burgundy plush velvet. A stunning intricate gold twisting clasp secures the front flap.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLFlapChainVelvetBurgundyT4.webp", "store": "storeBags", "slot": "bags", "silhouette": "flap-bag", "baseLooks": 9, "price": 460, "quality": "Premium", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant"], "npcAppeal": {"elite": 4}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_shopper_transparent_black_trim_t2", "name": "Clear Shopper with Black Trim", "brand": "Luxe Leather", "desc": "A trendy stadium-approved shopper tote made entirely of heavy-duty crystal-clear PVC, lined and trimmed neatly with stiff black leather edges and handles.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLShopperTransparentBlackTrimT2.webp", "store": "storeBags", "slot": "bags", "silhouette": "shopper", "baseLooks": 6, "price": 360, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_crossbody_saddle_tan_t3", "name": "Tan Saddle Crossbody", "brand": "Luxe Leather", "desc": "A classic equestrian-inspired saddle crossbody bag. Smooth tan leather featuring a distinctive curved base and an oversized brass front ring and equestrian strap closure.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLCrossbodySaddleTanT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "crossbody", "baseLooks": 7, "price": 460, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_top_handle_snake_print_t4", "name": "Snakeskin Top-Handle Brief", "brand": "Luxe Leather", "desc": "An aggressively structured professional briefcase-style bag encased in exotic faux python snake-print leather in shades of taupe and grey.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLTopHandleSnakePrintT4.webp", "store": "storeBags", "slot": "bags", "silhouette": "top-handle", "baseLooks": 8, "price": 500, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "daring", "elegant"], "npcAppeal": {"elite": 4, "professional": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_clutch_envelope_satin_navy_t3", "name": "Navy Satin Evening Clutch", "brand": "Luxe Leather", "desc": "A sleek minimalist evening envelope clutch swathed in rich navy blue satin. Framed by a subtle silver metal border edge.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLClutchEnvelopeSatinNavyT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "clutch", "baseLooks": 8, "price": 425, "quality": "Rare", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 3}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_slouchy_nylon_puffer_olive_t2", "name": "Olive Nylon Puffer Tote", "brand": "Luxe Leather", "desc": "An ultra-casual, cloud-like slouchy tote made of quilted, olive-green padded nylon. Super lightweight, durable, and highly practical.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLSlouchyNylonPufferOliveT2.webp", "store": "storeBags", "slot": "bags", "silhouette": "tote", "baseLooks": 5, "price": 395, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_drawstring_leather_ivory_t3", "name": "Ivory Leather Drawstring", "brand": "Luxe Leather", "desc": "A soft ivory pebble leather bucket variation featuring thick leather drawstrings sliding through polished gold grommets.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLDrawstringLeatherIvoryT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "bucket", "baseLooks": 7, "price": 425, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "casual"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_top_handle_bamboo_woven_t3", "name": "Woven Bamboo Summer Handbag", "brand": "Luxe Leather", "desc": "An incredibly chic summer statement accessory. Crafted from deeply woven caramel rattan wicker, finished with a distinct circular real bamboo top handle.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLTopHandleBambooWovenT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "top-handle", "baseLooks": 8, "price": 400, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "casual", "cute"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_clutch_acrylic_marbled_teal_t4", "name": "Marbled Teal Box Clutch", "brand": "Luxe Leather", "desc": "A breathtaking rigid box clutch made from heavy poured resin acrylic mimicking polished marbled stone in vivid teal and swirled gold veins. The ultimate conversation starter.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLClutchAcrylicMarbledTealT4.webp", "store": "storeBags", "slot": "bags", "silhouette": "clutch", "baseLooks": 10, "price": 495, "quality": "Luxury", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "daring"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_tote_perforated_leather_white_t3", "name": "White Perforated Leather Tote", "brand": "Luxe Leather", "desc": "A large bright white structured tote bag created from stiff matte leather completely covered in laser-cut micro-perforations.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLTotePerforatedLeatherWhiteT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "tote", "baseLooks": 7, "price": 440, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 75, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "professional"], "npcAppeal": {"professional": 3}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_backpack_convertible_olive_t3", "name": "Olive Convertible Backpack", "brand": "Luxe Leather", "desc": "An ingenious unstructured slouchy leather bag in deep olive. Multiple clip rings allow it to seamlessly convert from a bucket hobo to a stylish backpack.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLBackpackConvertibleOliveT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "backpack", "baseLooks": 7, "price": 460, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_clutch_foldover_croc_black_t4", "name": "Black Croc Foldover Clutch", "brand": "Luxe Leather", "desc": "A slick oversized portfolio clutch rendered in jet black crocodile leather. It folds elegantly in half and tucks under the arm for severe evening sleekness.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLClutchFoldoverCrocBlackT4.webp", "store": "storeBags", "slot": "bags", "silhouette": "clutch", "baseLooks": 9, "price": 520, "quality": "Luxury", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional", "daring"], "npcAppeal": {"elite": 4}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_shoulder_nylon_chain_black_t2", "name": "Nylon & Chain Bag", "brand": "Luxe Leather", "desc": "Fusing casual streetwear with luxury. This simple black nylon shoulder bag sits snugly under the arm but swings a heavy decorative silver chain across the front.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLShoulderNylonChainBlackT2.webp", "store": "storeBags", "slot": "bags", "silhouette": "shoulder", "baseLooks": 7, "price": 450, "quality": "Rare", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["daring", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_micro_chain_bag_silver_t4", "name": "Silver Micro Flap", "brand": "Luxe Leather", "desc": "A comically tiny micro handbag capable of holding a credit card and a lipstick. Rendered in dazzling metallic silver with an intricate hair-thin braided chain.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLMicroChainBagSilverT4.webp", "store": "storeBags", "slot": "bags", "silhouette": "crossbody", "baseLooks": 9, "price": 425, "quality": "Premium", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring", "elegant"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bag_ll_crossbody_guitar_strap_tan_t3", "name": "Tan Webbing Crossbody", "brand": "Luxe Leather", "desc": "A sleek, no-nonsense curved tan leather pouch elevated aggressively by an incredibly wide interchangeable canvas guitar-style shoulder strap boasting geometric stripes.", "image": "assets/content/clothing/bags/LuxeLeather/bagLLCrossbodyGuitarStrapTanT3.webp", "store": "storeBags", "slot": "bags", "silhouette": "crossbody", "baseLooks": 7, "price": 460, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* bikini bottoms DB reset (panty slot) */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.bikiniBottoms = [
{"id": "bikini_bot_nl_basic_brief_navy_t1", "name": "Basic Navy Brief", "brand": "Northline", "desc": "A simple, unadorned navy blue bikini bottom. Standard cut, decent coverage, gets the job done cheaply.", "image": "assets/content/clothing/bikiniBottom/Northline/bikiniBotNLBasicBriefNavyT1.webp", "store": "storeClothingA", "slot": "bikiniBottom", "silhouette": "brief", "baseLooks": 1, "price": 10, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "matchSet": "nlBasicNavySet", "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_nl_sport_boyshort_black_t2", "name": "Black Sport Boyshort", "brand": "Northline", "desc": "Athletic black boyshort bikini bottom. Provides maximum coverage and a secure fit for active swimming or beach volleyball.", "image": "assets/content/clothing/bikiniBottom/Northline/bikiniBotNLSportBoyshortBlackT2.webp", "store": "storeClothingA", "slot": "bikiniBottom", "silhouette": "boyshort", "baseLooks": 2, "price": 15, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 2, "durability": 60, "reqCorruption": 1, "matchSet": "nlSportBlackSet", "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_classic_tie_coral_t1", "name": "Coral Tie-Side Bottom", "brand": "Aqua Lane", "desc": "Classic bikini bottom in vibrant coral. Thin adjustable string ties at the hips, moderate back coverage.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALClassicTieCoralT1.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "tie-side", "baseLooks": 2, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "matchSet": "alCoralSolidSet", "tags": ["sporty", "cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_stripe_navy_boy_t1", "name": "Navy Stripe Boyshort", "brand": "Aqua Lane", "desc": "Sporty boyshort bikini bottom featuring navy and white horizontal stripes. Sits low on the hips and provides full secure coverage.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALStripeNavyBoyT1.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "boyshort", "baseLooks": 2, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "matchSet": "alNavyStripeSet", "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_tropical_ruched_t1", "name": "Tropical Ruched Bottom", "brand": "Aqua Lane", "desc": "Standard bikini bottom with a bold tropical palm and hibiscus print. Ruched side details add a cute flattering touch without exposing too much.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALTropicalRuchedT1.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "ruched", "baseLooks": 3, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_geometric_high_cut_t2", "name": "Geometric High-Cut Bottom", "brand": "Aqua Lane", "desc": "80s-inspired high-cut bikini bottom in a bold black and white geometric abstract print. Elongates the legs while maintaining mid-coverage.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALGeometricHighCutT2.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "high-cut", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "matchSet": "alGeometricBWSet", "tags": ["cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_floral_cheeky_t2", "name": "Floral Cheeky Bottom", "brand": "Aqua Lane", "desc": "Flirty cheeky-cut bikini bottom in a vibrant tropical floral print. Shows off a bit more skin at the back without being a full thong.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALFloralCheekyT2.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "cheeky", "baseLooks": 4, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_color_block_hipster_t2", "name": "Color-Block Hipster Bottom", "brand": "Aqua Lane", "desc": "Sporty low-rise hipster bikini bottom. Vibrant teal body with thick yellow side bands and white piping.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALColorBlockHipsterT2.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "hipster", "baseLooks": 4, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "matchSet": "alColorBlockTealSet", "tags": ["cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_ring_string_black_t3", "name": "Ring-Detail String Bottom", "brand": "Aqua Lane", "desc": "Sleek solid black string bikini bottom. The thin side straps connect to the front and back panels using shiny gold ring hardware.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALRingStringBlackT3.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "string", "baseLooks": 5, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 3, "matchSet": "alBlackRingSet", "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_mesh_panel_neon_yellow_t3", "name": "Neon Yellow Mesh-Panel Bottom", "brand": "Aqua Lane", "desc": "High-cut sport bikini bottom in vivid neon yellow. Features unpadded sheer black mesh cut-out panels on the hips for an edgy athletic look.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALMeshPanelNeonYellowT3.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "high-cut", "baseLooks": 5, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 60, "reqCorruption": 4, "matchSet": "alNeonYellowMeshSet", "tags": ["sporty", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_lace_up_side_leopard_t3", "name": "Leopard Lace-Up Bottom", "brand": "Aqua Lane", "desc": "Fierce leopard print cheeky bikini bottom. The sides feature sexy corset-style crisscross lace-up strings instead of solid fabric.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALLaceUpSideLeopardT3.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "cheeky", "baseLooks": 6, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "matchSet": "alLeopardSet", "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_micro_thong_coral_t4", "name": "Micro Thong Coral Bottom", "brand": "Aqua Lane", "desc": "The ultimate beach exhibitionist piece. An ultra-revealing vivid coral pink micro thong bottom consisting of just a tiny fabric patch and dental floss-thin strings.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALMicroThongCoralT4.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "thong", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 9, "exposureLevel": 9, "durability": 60, "reqCorruption": 6, "matchSet": "alMicroCoralSet", "tags": ["sexy", "revealing", "lewd"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_wrap_tie_white_t4", "name": "White Wrap-Tie Bottom", "brand": "Aqua Lane", "desc": "Cheeky white bikini bottom featuring extra-long side ties that wrap and crisscross dramatically around the thighs.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALWrapTieWhiteT4.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "tie-side", "baseLooks": 6, "price": 50, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "matchSet": "alWhiteWrapSet", "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_zip_scuba_black_t4", "name": "Scuba Zip-Detail Bottom", "brand": "Aqua Lane", "desc": "Athletic black neoprene scuba bikini bottom. High cut, thick fabric, featuring shiny silver faux zipper pockets on the front hips.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALZipScubaBlackT4.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "high-cut", "baseLooks": 7, "price": 55, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 80, "reqCorruption": 2, "matchSet": "alScubaBlackSet", "tags": ["sporty", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_cut_out_asymmetric_red_t4", "name": "Asymmetric Red Cut-Out Bottom", "brand": "Aqua Lane", "desc": "Avant-garde bright red bikini bottom with a daring diagonal hip strap design, leaving a large cut-out gap on one side of the pelvis.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALCutOutAsymmetricRedT4.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "asymmetric", "baseLooks": 7, "price": 65, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 60, "reqCorruption": 3, "matchSet": null, "tags": ["revealing", "sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 85, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_bot_al_chain_side_gold_t4", "name": "Gold Chain-Side Bottom", "brand": "Aqua Lane", "desc": "Peak luxury swimwear. Minimal front and back panels of metallic liquid gold fabric connected directly by heavy gold-plated chain links.", "image": "assets/content/clothing/bikiniBottom/AquaLane/bikiniBotALChainSideGoldT4.webp", "store": "storeSwim", "slot": "bikiniBottom", "silhouette": "string", "baseLooks": 8, "price": 105, "quality": "Luxury", "warmth": 0, "sexinessScore": 9, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "matchSet": "alGoldChainSet", "tags": ["sexy", "revealing", "elegant"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* bikini tops DB reset (bra slot) */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.bikiniTops = [
{"id": "bikini_top_nl_basic_triangle_navy_t1", "name": "Basic Navy Triangle", "brand": "Northline", "desc": "A very simple, unpadded navy blue string bikini top. Standard affordable swimwear for a quick trip to the beach.", "image": "assets/content/clothing/bikiniTop/Northline/bikiniTopNLBasicTriangleNavyT1.webp", "store": "storeClothingA", "slot": "bikiniTop", "silhouette": "triangle", "baseLooks": 1, "price": 15, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "matchSet": "nlBasicNavySet", "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_nl_sport_crop_black_t2", "name": "Black Sport Crop Top", "brand": "Northline", "desc": "Athletic black bikini crop top extending slightly further down the ribs. Thick straps ensure everything stays securely in place during activities.", "image": "assets/content/clothing/bikiniTop/Northline/bikiniTopNLSportCropBlackT2.webp", "store": "storeClothingA", "slot": "bikiniTop", "silhouette": "crop", "baseLooks": 2, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 3, "durability": 60, "reqCorruption": 1, "matchSet": "nlSportBlackSet", "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_triangle_solid_coral_t1", "name": "Coral Triangle Bikini Top", "brand": "Aqua Lane", "desc": "Classic triangle bikini top in vibrant coral. Thin adjustable tie straps at neck and back, minimal coverage, simple and sporty.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALTriangleSolidCoralT1.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "triangle", "baseLooks": 2, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "matchSet": "alCoralSolidSet", "tags": ["sporty", "cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_stripe_navy_racer_t1", "name": "Navy Stripe Racerback Bikini Top", "brand": "Aqua Lane", "desc": "Sporty racerback bikini crop top in navy and white horizontal stripes. Provides fuller coverage and support for active water sports.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALStripeNavyRacerT1.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "racerback", "baseLooks": 2, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 3, "durability": 60, "reqCorruption": 1, "matchSet": "alNavyStripeSet", "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_tropical_bandeau_t1", "name": "Tropical Print Bandeau Top", "brand": "Aqua Lane", "desc": "Strapless bandeau bikini top featuring a bold tropical palm and hibiscus print. Removable soft cups for optional support.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALTropicalBandeauT1.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "bandeau", "baseLooks": 3, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_geometric_halter_t2", "name": "Geometric Print Halter Top", "brand": "Aqua Lane", "desc": "Halter-neck triangle bikini top in a bold black and white geometric abstract print. Adjustable neck tie, moderate coverage.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALGeometricHalterT2.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "halter-triangle", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "matchSet": "alGeometricBWSet", "tags": ["cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_floral_underwire_t2", "name": "Floral Underwire Bikini Top", "brand": "Aqua Lane", "desc": "Structured underwire bikini top in a vibrant tropical floral print. Wide straps and underwire cups provide excellent support and shape.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALFloralUnderwireT2.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "underwire", "baseLooks": 4, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_color_block_tie_front_t2", "name": "Color-Block Tie-Front Bikini Top", "brand": "Aqua Lane", "desc": "Vibrant color-block bikini top with a central tie-front knot. Bold teal and yellow panels separated by a white border. Adjustable halter tie.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALColorBlockTieFrontT2.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "tie-front", "baseLooks": 4, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "matchSet": "alColorBlockTealSet", "tags": ["cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_ring_halter_solid_black_t3", "name": "Ring-Detail Halter Bikini Top", "brand": "Aqua Lane", "desc": "Sleek solid black halter bikini top with a gold ring hardware detail at the center bust. Minimal and sexy poolside statement piece.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALRingHalterSolidBlackT3.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "halter-triangle", "baseLooks": 5, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 6, "durability": 60, "reqCorruption": 2, "matchSet": "alBlackRingSet", "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_underwire_neon_yellow_t3", "name": "Neon Yellow Underwire Top", "brand": "Aqua Lane", "desc": "Supportive underwire balcony bikini top in high-vis neon yellow. The unpadded sheer mesh inserts at the top of the cups add a daring sporty edge.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALUnderwireNeonYellowT3.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "underwire", "baseLooks": 5, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "matchSet": "alNeonYellowMeshSet", "tags": ["sporty", "sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "bikini_top_al_lace_up_front_leopard_t3", "name": "Leopard Lace-Up Top", "brand": "Aqua Lane", "desc": "Fierce leopard print bandeau top featuring a sexy lace-up corset detail between the bust. Detachable halter string included.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALLaceUpFrontLeopardT3.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "lace-up", "baseLooks": 6, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 3, "matchSet": "alLeopardSet", "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_micro_halter_coral_t4", "name": "Micro Triangle Coral Top", "brand": "Aqua Lane", "desc": "Ultra-revealing micro triangle bikini top in vibrant coral pink. The ultimate beach exhibitionist piece, covering just the absolute minimum.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALMicroHalterCoralT4.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "triangle", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 6, "matchSet": "alMicroCoralSet", "tags": ["sexy", "revealing", "lewd"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "bikini_top_al_wrap_around_white_t4", "name": "White Wrap-Around Halter", "brand": "Aqua Lane", "desc": "Stunning pure white halter top with extra-long ties that wrap multiple times around the waist. Designed for striking tan lines and poolside lounging.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALWrapAroundWhiteT4.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "halter-triangle", "baseLooks": 6, "price": 55, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 60, "reqCorruption": 4, "matchSet": "alWhiteWrapSet", "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_zip_front_scuba_black_t4", "name": "Scuba Zip-Front Crop", "brand": "Aqua Lane", "desc": "Athletic black neoprene scuba crop top with a functional shiny silver front zipper. Zip it down to transition from sporty to seriously sexy.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALZipFrontScubaBlackT4.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "crop", "baseLooks": 7, "price": 60, "quality": "Premium", "warmth": 1, "sexinessScore": 8, "exposureLevel": 6, "durability": 80, "reqCorruption": 4, "matchSet": "alScubaBlackSet", "tags": ["sporty", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_cut_out_asymmetric_red_t4", "name": "Asymmetric Red Cut-Out", "brand": "Aqua Lane", "desc": "Avant-garde bright red bikini top featuring a one-shoulder diagonal design with an extreme slash cut-out across the chest.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALCutOutAsymmetricRedT4.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "asymmetric", "baseLooks": 7, "price": 75, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["revealing", "sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 85, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bikini_top_al_chain_strap_gold_t4", "name": "Gold Chain-Strap Top", "brand": "Aqua Lane", "desc": "The ultimate luxury statement piece. Minimal triangles of shimmering metallic gold fabric suspended by actual gold-plated chain-link straps.", "image": "assets/content/clothing/bikiniTop/AquaLane/bikiniTopALChainStrapGoldT4.webp", "store": "storeSwim", "slot": "bikiniTop", "silhouette": "triangle", "baseLooks": 8, "price": 120, "quality": "Luxury", "warmth": 0, "sexinessScore": 9, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "matchSet": "alGoldChainSet", "tags": ["sexy", "revealing", "elegant"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* Bodysuits DB reset */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.bodysuits = [
{"id": "body_nl_basic_cotton_black_t1", "name": "Basic Black Cotton Bodysuit", "brand": "Northline", "desc": "A simple everyday cotton bodysuit in solid black. Scoop neck, thin straps, and moderate coverage. Comfortable and functional as a base layer.", "image": "assets/content/clothing/bodysuit/Northline/bodyNLBasicCottonBlackT1.webp", "store": "storeClothingA", "slot": "bodysuit", "silhouette": "scoop-neck", "baseLooks": 2, "price": 15, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 3, "durability": 60, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_nl_sport_zip_front_navy_t2", "name": "Navy Zip-Front Sport Bodysuit", "brand": "Northline", "desc": "Athletic navy blue bodysuit made of stretchy moisture-wicking fabric. Features a functional silver zipper running down the chest and thick supportive racerback straps.", "image": "assets/content/clothing/bodysuit/Northline/bodyNLSportZipFrontNavyT2.webp", "store": "storeClothingA", "slot": "bodysuit", "silhouette": "racerback", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 1, "sexinessScore": 2, "exposureLevel": 3, "durability": 60, "reqCorruption": 1, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_nl_ribbed_long_sleeve_grey_t2", "name": "Grey Ribbed LS Bodysuit", "brand": "Northline", "desc": "A cozy, form-fitting heather grey bodysuit with long sleeves and a subtle ribbed knit texture. Perfect for cooler days and layering underneath jeans.", "image": "assets/content/clothing/bodysuit/Northline/bodyNLRibbedLongSleeveGreyT2.webp", "store": "storeClothingA", "slot": "bodysuit", "silhouette": "long-sleeve", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 1, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_nl_seamless_athletic_white_t2", "name": "White Seamless Sport Suit", "brand": "Northline", "desc": "A bright white, seamless compression bodysuit delivering a slick, distraction-free fit for workout routines. It holds everything firmly like a second skin.", "image": "assets/content/clothing/bodysuit/Northline/bodyNLSeamlessAthleticWhiteT2.webp", "store": "storeClothingA", "slot": "bodysuit", "silhouette": "compression", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 1, "sexinessScore": 2, "exposureLevel": 2, "durability": 75, "reqCorruption": 1, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_vm_satin_wrap_emerald_t3", "name": "Emerald Satin Wrap Bodysuit", "brand": "VERA Mode", "desc": "An elegant casual-chic structured bodysuit tailored from luxurious emerald green satin. It features a draped wrap front that creates a flattering V-neckline.", "image": "assets/content/clothing/bodysuit/VeraMode/bodyVMSatinWrapEmeraldT3.webp", "store": "storeClothingB", "slot": "bodysuit", "silhouette": "wrap", "baseLooks": 4, "price": 45, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "casual"], "npcAppeal": {"elite": 2, "professional": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_vm_cut_out_knit_tan_t3", "name": "Tan Cut-Out Knit Bodysuit", "brand": "VERA Mode", "desc": "A trendy asymmetric fine-knit bodysuit in soft warm tan. One shoulder features a sharp geometric cutout exposing the collarbone.", "image": "assets/content/clothing/bodysuit/VeraMode/bodyVMCutOutKnitTanT3.webp", "store": "storeClothingB", "slot": "bodysuit", "silhouette": "asymmetric", "baseLooks": 4, "price": 40, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 4, "durability": 60, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "daring", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_vm_jersey_plunge_black_t3", "name": "Black Plunge Jersey Bodysuit", "brand": "VERA Mode", "desc": "A sleek, dramatically bold jersey bodysuit in matte black. The neckline plunges deeply down the center of the chest for serious evening appeal.", "image": "assets/content/clothing/bodysuit/VeraMode/bodyVMJerseyPlungeBlackT3.webp", "store": "storeClothingB", "slot": "bodysuit", "silhouette": "plunge", "baseLooks": 5, "price": 40, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 60, "reqCorruption": 5, "matchSet": null, "tags": ["sexy", "elegant", "revealing"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_vm_leopard_mesh_sleeve_t4", "name": "Leopard Print Mesh Bodysuit", "brand": "VERA Mode", "desc": "A fierce, head-turning statement piece. This bodysuit features a striking leopard print base beautifully contrasted with long, billowy sheer black mesh sleeves.", "image": "assets/content/clothing/bodysuit/VeraMode/bodyVMLeopardMeshSleeveT4.webp", "store": "storeClothingB", "slot": "bodysuit", "silhouette": "mesh-sleeve", "baseLooks": 6, "price": 50, "quality": "Premium", "warmth": 1, "sexinessScore": 6, "exposureLevel": 5, "durability": 60, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "elegant"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_is_lace_plunge_black_t3", "name": "Black Lace Plunge Bodysuit", "brand": "Intimate Secrets", "desc": "An elegant yet seductive black lace bodysuit. Unlined sheer scalloped lace traces an extreme plunging V-neckline down to the navel.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISLacePlungeBlackT3.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "lace-plunge", "baseLooks": 6, "price": 65, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 60, "reqCorruption": 6, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "body_is_strappy_harness_red_t4", "name": "Red Strappy Harness Suit", "brand": "Intimate Secrets", "desc": "A provocative blood-red lingerie set mimicking a full one-piece bodysuit. Features an aggressive geometric cage harness structure strapping tightly over the upper chest.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISStrappyHarnessRedT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "harness", "baseLooks": 7, "price": 85, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 60, "reqCorruption": 8, "matchSet": null, "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 100, "reqExhibitionism": 90, "reqHeelsSkill": 0},
{"id": "body_is_sheer_fishnet_black_t4", "name": "Black Full Fishnet Bodystocking", "brand": "Intimate Secrets", "desc": "A seamless stretchy full-body black fishnet stocking with long sleeves. Completely transparent, hugging every curve like a second skin while hiding absolutely nothing.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISSheerFishnetBlackT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "fishnet", "baseLooks": 6, "price": 55, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 8, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 100, "reqExhibitionism": 90, "reqHeelsSkill": 0},
{"id": "body_is_vinyl_zip_front_black_t4", "name": "Black Vinyl Zip Suit", "brand": "Intimate Secrets", "desc": "Fusing fetishwear with fashion. This glossy, skin-tight black liquid vinyl bodysuit features a functional silver zipper running straight down from neck to navel.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISVinylZipFrontBlackT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "vinyl-zip", "baseLooks": 8, "price": 110, "quality": "Premium", "warmth": 0, "sexinessScore": 9, "exposureLevel": 6, "durability": 60, "reqCorruption": 6, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_is_crotchless_mesh_lace_t5", "name": "Crotchless Lace Illusion", "brand": "Intimate Secrets", "desc": "Unapologetically lewd statement lingerie. An intricate black mesh and lace bodice that leaves you completely exposed with an entirely open crotch design.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISCrotchlessMeshLaceT5.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "crotchless", "baseLooks": 8, "price": 135, "quality": "Premium", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 60, "reqCorruption": 6, "matchSet": null, "tags": ["erotic", "lewd", "slutty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 100, "reqExhibitionism": 60, "reqHeelsSkill": 0},
{"id": "body_is_cut_out_oring_emerald_t4", "name": "Emerald O-Ring Cut-Outs", "brand": "Intimate Secrets", "desc": "A striking dark emerald green one-piece. The sides of the waist and the entire sternum are missing, replaced by thin elastic cords spanning through oversized gold O-rings.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISCutOutOringEmeraldT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "cut-out", "baseLooks": 7, "price": 90, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 60, "reqCorruption": 6, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_is_mini_suspender_black_t4", "name": "Black Suspender Straps Suit", "brand": "Intimate Secrets", "desc": "An editorial lingerie bodysuit in black satin, tailored very high on the hip. Four adjustable mini suspender garter clips dangle from the hem for attaching thigh-highs.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISMiniSuspenderBlackT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "garter-bodysuit", "baseLooks": 7, "price": 105, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "elegant", "daring"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_is_velvet_burnout_red_t3", "name": "Red Velvet Burnout Bodysuit", "brand": "Intimate Secrets", "desc": "Luxurious jewel-toned crimson red bodysuit boasting a velvet burnout floral pattern on a sheer mesh base. Romantic, tactile, and lightly see-through.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISVelvetBurnoutRedT3.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "velvet-burnout", "baseLooks": 6, "price": 75, "quality": "Premium", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_is_pearl_detail_thong_t5", "name": "Crotchless Pearl Bodysuit", "brand": "Intimate Secrets", "desc": "The apex of erotic exhibitionism. An impossibly skimpy white satin and tape bodysuit where the crotch is completely open save for a single strand of faux pearls.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISPearlDetailThongT5.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "crotchless", "baseLooks": 9, "price": 145, "quality": "Premium", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 60, "reqCorruption": 8, "matchSet": null, "tags": ["erotic", "lewd", "slutty"], "npcAppeal": {"elite": 3}, "shopAvailable": true, "startOwned": false, "reqConfidence": 100, "reqExhibitionism": 90, "reqHeelsSkill": 0},
{"id": "body_is_nude_illusion_tattoo_t4", "name": "Nude Illusion Tattoo Suit", "brand": "Intimate Secrets", "desc": "A breathtaking high-neck bodysuit made of completely transparent skin-toned mesh, heavily embroidered with sprawling black floral \"tattoo\" lacework.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISNudeIllusionTattooT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "illusion", "baseLooks": 7, "price": 95, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 6, "matchSet": null, "tags": ["sexy", "elegant", "daring"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 50, "reqHeelsSkill": 0},
{"id": "body_is_silk_wrap_champagne_t3", "name": "Champagne Silk Bodysuit", "brand": "Intimate Secrets", "desc": "A romantically loose wrap-style bodysuit in shimmering champagne real silk. Effortlessly elegant for layering or late-night lounging.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISSilkWrapChampagneT3.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "wrap", "baseLooks": 6, "price": 80, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "casual"], "npcAppeal": {"rich": 3}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_is_corset_boning_white_lace_t4", "name": "White Corset Bodysuit", "brand": "Intimate Secrets", "desc": "A fiercely structured bridal-inspired white lace corset bodysuit. Heavy vertical boning aggressively cinches the waist, pushing the bust up via sheer lace demi-cups.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISCorsetBoningWhiteLaceT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "corset", "baseLooks": 7, "price": 110, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 5, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_is_gunmetal_chainmail_t5", "name": "Gunmetal Chainmail Suit", "brand": "Intimate Secrets", "desc": "Heavy, cold, and undeniably glamorous. A plunging halter bodysuit crafted entirely from slinky, fluid gunmetal silver liquid chainmail mesh.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISGunmetalChainmailT5.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "plunge", "baseLooks": 9, "price": 185, "quality": "Luxury", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 60, "reqCorruption": 6, "matchSet": null, "tags": ["elegant", "daring", "sexy"], "npcAppeal": {"elite": 4}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "body_is_turtleneck_fishnet_neon_pink_t4", "name": "Neon Pink Fishnet Bodystocking", "brand": "Intimate Secrets", "desc": "A shocking neon pink full-sleeve bodystocking. The high turtleneck collar offsets the fact that the open diamond fishnet exposes absolutely everything underneath.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISTurtleneckFishnetNeonPinkT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "fishnet", "baseLooks": 6, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 8, "matchSet": null, "tags": ["revealing", "sexy", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 85, "reqExhibitionism": 90, "reqHeelsSkill": 0},
{"id": "body_is_underwire_balcony_leopard_t4", "name": "Leopard Balcony Bodysuit", "brand": "Intimate Secrets", "desc": "A structured, fiercely supportive leopard print bodysuit. Heavy underwire balcony cups thrust the bust upward, separated by sheer black mesh side panels.", "image": "assets/content/clothing/bodysuit/IntimateSecrets/bodyISUnderwireBalconyLeopardT4.webp", "store": "storeLingerieB", "slot": "bodysuit", "silhouette": "underwire-balcony", "baseLooks": 7, "price": 85, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 5, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* bottoms Database */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.bottoms = [
{"id":"jeans_blue_owned","name":"Blue Everyday Jeans","brand":"","desc":"Comfortable blue jeans for everyday wear.","image":"assets/content/clothing/bottoms/Starter/jeans_blue_owned.webp","store":"","slot":"bottom","silhouette":"straight jeans","baseLooks":1,"price":0,"quality":"Common","warmth":0,"sexinessScore":0,"exposureLevel":0,"durability":100,"reqCorruption":0,"matchSet":null,"tags":["casual","basic","mild"],"npcAppeal":{},"shopAvailable":false,"startOwned":true,"reqConfidence":0},
{"id":"leggings_black_owned","name":"Black Everyday Leggings","brand":"","desc":"Simple black leggings for everyday wear.","image":"assets/content/clothing/bottoms/Starter/leggings_black_owned.webp","store":"","slot":"bottom","silhouette":"leggings","baseLooks":1,"price":0,"quality":"Common","warmth":0,"sexinessScore":0,"exposureLevel":0,"durability":100,"reqCorruption":0,"matchSet":null,"tags":["casual","basic","sporty"],"npcAppeal":{},"shopAvailable":false,"startOwned":true,"reqConfidence":0},
{"id": "bottom_mom_jeans_blue", "name": "Classic Blue Mom Jeans", "brand": "Northline Apparel", "desc": "A pair of classic solid blue high-waisted mom jeans crafted from sturdy non-stretch denim, featuring a relaxed tapered fit.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomMomJeansBlue.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 4, "price": 35, "quality": "Common", "warmth": 4, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_acid_wash_mini_skirt", "name": "Acid Wash Denim Mini", "brand": "Northline Apparel", "desc": "A trendy slightly distressed acid-wash denim mini skirt with a raw hem and classic five-pocket styling.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomAcidWashMiniSkirt.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "mini", "baseLooks": 4, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 85, "reqCorruption": 3, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_floral_midi_skirt", "name": "Ditsy Floral Midi Skirt", "brand": "Northline Apparel", "desc": "A lightweight ditsy subtle floral print midi skirt made of soft rayon jersey, dropping to mid-calf with a gentle side slit.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomFloralMidiSkirt.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "midi", "baseLooks": 6, "price": 40, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 1, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_cargo_pants_beige", "name": "Beige Parachute Cargo Pants", "brand": "Northline Apparel", "desc": "A pair of highly relaxed beige cotton utility cargo pants featuring oversized side pockets and drawstring ankle cuffs.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomCargoPantsBeige.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 5, "price": 55, "quality": "Common", "warmth": 5, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_pleated_tennis_skirt_white", "name": "White Pleated Tennis Skirt", "brand": "Northline Apparel", "desc": "A classic bright white short tennis skirt structured with rigid all-around box pleats that flare out gently.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomPleatedTennisSkirtWhite.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "mini", "baseLooks": 5, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 85, "reqCorruption": 3, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_ribbed_knit_maxi_grey", "name": "Grey Ribbed Knit Maxi Skirt", "brand": "Northline Apparel", "desc": "A soft heather grey heavy ribbed-knit maxi skirt that firmly hugs the body all the way down to the ankle.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomRibbedKnitMaxiGrey.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "maxi", "baseLooks": 5, "price": 45, "quality": "Rare", "warmth": 3, "sexinessScore": 3, "exposureLevel": 1, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_faux_leather_pants_black", "name": "Straight Leg Faux Leather Pants", "brand": "Northline Apparel", "desc": "A sleek pair of matte black faux leather trousers boasting a high waist and a timeless straight leg cut.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomFauxLeatherPantsBlack.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "straight", "baseLooks": 7, "price": 60, "quality": "Rare", "warmth": 3, "sexinessScore": 5, "exposureLevel": 5, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_denim_shorts_distressed", "name": "Distressed Mid-Thigh Denim Shorts", "brand": "Northline Apparel", "desc": "A casual pair of light wash mid-thigh denim shorts featuring frayed hems and heavy distressing across the front pockets.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomDenimShortsDistressed.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 4, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 75, "reqCorruption": 3, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_linen_wide_leg_navy", "name": "Navy Wide-Leg Linen Pants", "brand": "Northline Apparel", "desc": "A breathable and relaxed pair of unlined navy blue linen-blend wide-leg pants featuring a comfortable wide elastic stretch waistband.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomLinenWideLegNavy.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "wide-leg", "baseLooks": 6, "price": 40, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_gingham_tiered_skirt", "name": "Red Gingham Tiered Mini", "brand": "Northline Apparel", "desc": "A playful mini skirt constructed in a classic red-and-white tight gingham pattern featuring distinct flowing gathered tiers.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomGinghamTieredSkirt.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "tiered", "baseLooks": 5, "price": 35, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_skinny_jeans_black", "name": "Black Wash Skinny Jeans", "brand": "Northline Apparel", "desc": "A trusty pair of deep washed black stretch denim skinny jeans, designed with a high rise waist that hugs tightly from hip to ankle.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomSkinnyJeansBlack.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "skinny", "baseLooks": 4, "price": 40, "quality": "Common", "warmth": 4, "sexinessScore": 2, "exposureLevel": 1, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_sweatpants_grey", "name": "Heather Grey Joggers", "brand": "Northline Apparel", "desc": "Relaxed fit heather grey cotton jogger sweatpants with an elastic ankle cuff and drawstring waist.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomSweatpantsGrey.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_corduroy_pants_brown", "name": "Brown Corduroy Pants", "brand": "Northline Apparel", "desc": "Warm brown corduroy straight leg pants offering a comfortable vintage everyday aesthetic.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomCorduroyPantsBrown.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "straight", "baseLooks": 5, "price": 40, "quality": "Common", "warmth": 5, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_tiered_maxi_white", "name": "White Boho Tiered Maxi Skirt", "brand": "Northline Apparel", "desc": "A flowy pristine white cotton boho maxi skirt with beautifully gathered tiers cascading to the floor.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomTieredMaxiWhite.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "tiered", "baseLooks": 6, "price": 40, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_biker_shorts_black", "name": "Black Spandex Biker Shorts", "brand": "Northline Apparel", "desc": "Basic black spandex biker shorts hitting mid-thigh, offering a highly stretchable athletic fit.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomBikerShortsBlack.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 4, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wrap_skirt_floral", "name": "Navy Floral Wrap Midi", "brand": "Northline Apparel", "desc": "A dark navy wrap midi skirt featuring a dainty floral print and a romantic side tie yielding a tasteful slit.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomWrapSkirtFloral.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "midi", "baseLooks": 7, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 6, "durability": 75, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_denim_skirt_midi", "name": "Denim Midi Skirt With Slit", "brand": "Northline Apparel", "desc": "A classic medium-blue wash heavy denim midi skirt characterized by a prominent central front slit for mobility.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomDenimSkirtMidi.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "midi", "baseLooks": 5, "price": 35, "quality": "Common", "warmth": 3, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 1, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_leather_mini_skirt", "name": "Faux Leather Zip Mini Skirt", "brand": "Northline Apparel", "desc": "A sleekly tailored black faux leather mini skirt featuring a gleaming silver hardware zipper straight down the front.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomLeatherMiniSkirt.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "mini", "baseLooks": 7, "price": 85, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 80, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_plaid_mini_skirt", "name": "Red Tartan Pleated Mini", "brand": "Northline Apparel", "desc": "A classic schoolgirl aesthetic bright red tartan plaid heavily pleated mini skirt.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomPlaidMiniSkirt.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "mini", "baseLooks": 5, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_lounge_shorts_grey", "name": "Soft Grey Lounge Shorts", "brand": "Northline Apparel", "desc": "Supremely soft and lightweight grey marl jersey lounge shorts outfitted with an elastic drawstring waist.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomLoungeShortsGrey.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 3, "price": 15, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_jeans_bootcut_blue", "name": "Vintage Bootcut Jeans", "brand": "Northline Apparel", "desc": "Classic vintage blue wash denim jeans structured cleanly with a subtle wide bootcut flare out gracefully at the bottom.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomJeansBootcutBlue.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "bootcut", "baseLooks": 5, "price": 40, "quality": "Common", "warmth": 5, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_cargo_shorts_khaki", "name": "Khaki Cargo Shorts", "brand": "Northline Apparel", "desc": "Practical durable khaki cotton utility cargo shorts boasting deeply roomy flap pockets firmly attached right on the thighs.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomCargoShortsKhaki.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 4, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 2, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_velour_sweatpants_pink", "name": "Pink Velour Track Pants", "brand": "Northline Apparel", "desc": "Y2K-inspired eye-catching bright pink velour track pants featuring a comfortable low rise fit.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomVelourSweatpantsPink.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 3, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_capri_pants_denim", "name": "Light Wash Denim Capris", "brand": "Northline Apparel", "desc": "A throwback pair of light wash denim capri pants hitting just below the knee with a slight side slit.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomCapriPantsDenim.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "capri", "baseLooks": 4, "price": 30, "quality": "Common", "warmth": 5, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 1, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wide_leg_sweatpants_black", "name": "Black Wide-Leg Sweatpants", "brand": "Northline Apparel", "desc": "Oversized, baggy black fleece sweatpants boasting a highly relaxed wide leg cut and an elastic waistband.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomWideLegSweatpantsBlack.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "wide-leg", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_pleated_midi_skirt_khaki", "name": "Khaki Pleated Midi Skirt", "brand": "Northline Apparel", "desc": "A classic khaki green midi skirt styled with sharp all-around accordion pleats.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomPleatedMidiSkirtKhaki.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "midi", "baseLooks": 6, "price": 40, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_high_low_skirt_black", "name": "Black High-Low Skirt", "brand": "Northline Apparel", "desc": "A distinctive black jersey skirt showcasing an asymmetrical high-low hemline, ending above the knee in the front and draping low in the back.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomHighLowSkirtBlack.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "asymmetrical", "baseLooks": 6, "price": 35, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_leggings_grey_marled", "name": "Marled Grey Leggings", "brand": "Northline Apparel", "desc": "Soft, thick, marled grey everyday leggings constructed with a wide, supportive stretch waistband.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomLeggingsGreyMarled.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "leggings", "baseLooks": 4, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 2, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_dolphin_shorts_blue", "name": "Blue Dolphin Shorts", "brand": "Northline Apparel", "desc": "Classic vibrant blue retro dolphin shorts featuring distinctive white binding along the edges and side seams.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomDolphinShortsBlue.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 4, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 85, "reqCorruption": 4, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_chino_shorts_beige", "name": "Beige Cotton Chino Shorts", "brand": "Northline Apparel", "desc": "Stiff and classic beige cotton chino-style shorts tailored cleanly for a polished casual look.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomChinoShortsBeige.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 5, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 2, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_bell_bottom_jeans_washed", "name": "Vintage Bell Bottom Jeans", "brand": "Northline Apparel", "desc": "Seventies-inspired vintage mid-wash denim jeans distinguished by an exaggerated wide bell bottom flare.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomBellBottomJeansWashed.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "flared", "baseLooks": 5, "price": 50, "quality": "Rare", "warmth": 5, "sexinessScore": 4, "exposureLevel": 2, "durability": 95, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_layered_ruffle_mini_pink", "name": "Pink Ruffle Mini Skirt", "brand": "Northline Apparel", "desc": "A sweet, light pink mini skirt constructed entirely of delicate overlapping chiffon ruffles.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomLayeredRuffleMiniPink.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "mini", "baseLooks": 7, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 70, "reqCorruption": 3, "matchSet": null, "tags": ["cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_paperbag_pants_olive", "name": "Olive Paperbag Waist Trousers", "brand": "Northline Apparel", "desc": "Pleated olive green woven trousers showcasing a distinctive paperbag waist tied with a matching fabric belt.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomPaperbagPantsOlive.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 6, "price": 45, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_jorts_denim", "name": "Classic Denim Jorts", "brand": "Northline Apparel", "desc": "Thick oversized blue denim jorts cut to hit squarely at the knee, possessing a rugged and incredibly laid-back silhouette.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomJortsDenim.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 3, "price": 35, "quality": "Common", "warmth": 3, "sexinessScore": 0, "exposureLevel": 0, "durability": 95, "reqCorruption": 1, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_maxi_skirt_denim", "name": "Denim Maxi Skirt", "brand": "Northline Apparel", "desc": "A timeless structured full-length blue denim maxi skirt detailed neatly with a subtle button front and a rear slit.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomMaxiSkirtDenim.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "maxi", "baseLooks": 5, "price": 40, "quality": "Common", "warmth": 4, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 1, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_track_shorts_navy", "name": "Navy Track Shorts", "brand": "Northline Apparel", "desc": "Lightweight navy blue nylon athletic track shorts outlined cleanly with white side trim.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomTrackShortsNavy.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 3, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 85, "reqCorruption": 3, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_sweatshorts_grey", "name": "Grey Sweatshorts", "brand": "Northline Apparel", "desc": "Exceptionally comfortable light grey cotton fleece sweatshorts ending high on the thigh with an unfitted raw hem.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomSweatshortsGrey.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "shorts", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 80, "reqCorruption": 5, "matchSet": null, "tags": ["casual", "sexy", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_mom_jeans_black", "name": "Black Mom Jeans", "brand": "Northline Apparel", "desc": "Classic high-waisted black denim mom jeans presenting a comfortable relaxed taper toward the ankle.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomMomJeansBlack.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 4, "price": 40, "quality": "Common", "warmth": 4, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wrap_skirt_linen", "name": "Beige Linen Wrap Skirt", "brand": "Northline Apparel", "desc": "A breathable and natural beige linen wrap skirt terminating securely right at the knee.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomWrapSkirtLinen.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "wrap", "baseLooks": 6, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_leggings_flared_black", "name": "Flared Yoga Leggings", "brand": "Northline Apparel", "desc": "Black high-stretch athletic yoga leggings cut flawlessly with a retro-inspired flare leg reaching cleanly to the shoe.", "image": "assets/content/clothing/bottoms/NorthlineApparel/bottomLeggingsFlaredBlack.webp", "store": "storeClothingA", "slot": "bottom", "silhouette": "flared", "baseLooks": 4, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 1, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_faux_leather_pants_black_vera", "name": "Black Faux Leather Pants", "brand": "VERA Mode", "desc": "Chic black faux leather straight-leg pants with a sleek, glossy finish.", "image": "assets/content/clothing/bottoms/VERAMode/bottomFauxLeatherPantsBlack.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "straight", "baseLooks": 7, "price": 60, "quality": "Common", "warmth": 3, "sexinessScore": 5, "exposureLevel": 5, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_satin_midi_skirt_green", "name": "Emerald Satin Midi Skirt", "brand": "VERA Mode", "desc": "A luxurious emerald green satin slip skirt that drapes beautifully over the figure.", "image": "assets/content/clothing/bottoms/VERAMode/bottomSatinMidiSkirtGreen.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "midi", "baseLooks": 8, "price": 95, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 70, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_tailored_shorts_white", "name": "White Tailored Shorts", "brand": "VERA Mode", "desc": "Crisp white high-waisted tailored shorts featuring front pleats and a polished look.", "image": "assets/content/clothing/bottoms/VERAMode/bottomTailoredShortsWhite.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "shorts", "baseLooks": 6, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 2, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_split_hem_leggings_black", "name": "Split Hem Black Leggings", "brand": "VERA Mode", "desc": "Trendy high-waisted black leggings exhibiting stylish front slit hems that graze the shoe.", "image": "assets/content/clothing/bottoms/VERAMode/bottomSplitHemLeggingsBlack.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "flared", "baseLooks": 5, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 1, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_cargo_pants_beige_vera", "name": "Wide-Leg Beige Cargos", "brand": "VERA Mode", "desc": "Trendy wide-leg beige cargo pants equipped with oversized utility pockets and a relaxed silhouette.", "image": "assets/content/clothing/bottoms/VERAMode/bottomCargoPantsBeige.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "wide-leg", "baseLooks": 5, "price": 55, "quality": "Common", "warmth": 5, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_ruched_mini_skirt_red", "name": "Red Ruched Mini Skirt", "brand": "VERA Mode", "desc": "A vibrant red bodycon mini skirt featuring flattering all-over ruching to accentuate curves.", "image": "assets/content/clothing/bottoms/VERAMode/bottomRuchedMiniSkirtRed.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "mini", "baseLooks": 6, "price": 50, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 75, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_vegan_leather_shorts_brown", "name": "Brown Vegan Leather Shorts", "brand": "VERA Mode", "desc": "Chic warm brown vegan leather shorts sporting a neat tailored fit and subtle sheen.", "image": "assets/content/clothing/bottoms/VERAMode/bottomVeganLeatherShortsBrown.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "shorts", "baseLooks": 6, "price": 45, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_plisse_trousers_gold", "name": "Gold Plissé Trousers", "brand": "VERA Mode", "desc": "Shimmering metallic gold plissé wide-leg trousers that catch the light effortlessly.", "image": "assets/content/clothing/bottoms/VERAMode/bottomPlisseTrousersGold.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "wide-leg", "baseLooks": 8, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 1, "durability": 70, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wrap_skirt_black", "name": "Black Asymmetric Skort", "brand": "VERA Mode", "desc": "An edgy black tailored skort displaying a sharp asymmetrical wrap front mimicking a mini skirt.", "image": "assets/content/clothing/bottoms/VERAMode/bottomWrapSkortBlack.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "skort", "baseLooks": 5, "price": 45, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 85, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_ribbed_knit_skirt_midi", "name": "Beige Ribbed Midi Skirt", "brand": "VERA Mode", "desc": "A soft beige ribbed knit midi skirt tailored directly to trace curves, highlighted with a side slit.", "image": "assets/content/clothing/bottoms/VERAMode/bottomRibbedKnitSkirtMidi.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "midi", "baseLooks": 6, "price": 40, "quality": "Common", "warmth": 2, "sexinessScore": 4, "exposureLevel": 3, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_distressed_straight_jeans_wash", "name": "Distressed Straight Jeans", "brand": "VERA Mode", "desc": "Light wash straight-leg jeans with trendy distressed knee rips for a casual chic vibe.", "image": "assets/content/clothing/bottoms/VERAMode/bottomDistressedStraightJeansWash.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "straight", "baseLooks": 4, "price": 50, "quality": "Common", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_sequined_mini_skirt_silver", "name": "Silver Sequined Mini Skirt", "brand": "VERA Mode", "desc": "A glamorous silver sequined mini skirt that sparkles brilliantly with every movement.", "image": "assets/content/clothing/bottoms/VERAMode/bottomSequinedMiniSkirtSilver.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "mini", "baseLooks": 8, "price": 60, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 65, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_latex_leggings_red", "name": "Red Latex Leggings", "brand": "VERA Mode", "desc": "Striking bright red latex leggings designed to tightly hug the body with a high-gloss finish.", "image": "assets/content/clothing/bottoms/VERAMode/bottomLatexLeggingsRed.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "leggings", "baseLooks": 7, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 5, "durability": 75, "reqCorruption": 4, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_asymmetric_midi_skirt_black", "name": "Asymmetric Midi Skirt", "brand": "VERA Mode", "desc": "A sleek black jersey midi skirt featuring a dramatic diagonal cut and an elegant drape.", "image": "assets/content/clothing/bottoms/VERAMode/bottomAsymmetricMidiSkirtBlack.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "asymmetrical", "baseLooks": 6, "price": 45, "quality": "Common", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_cutout_trousers_white", "name": "White Cutout Trousers", "brand": "VERA Mode", "desc": "Crisp white wide-leg trousers distinguished by stylish peek-a-boo cutouts resting openly along the hips.", "image": "assets/content/clothing/bottoms/VERAMode/bottomCutoutTrousersWhite.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "wide-leg", "baseLooks": 7, "price": 55, "quality": "Rare", "warmth": 3, "sexinessScore": 5, "exposureLevel": 5, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_vinyl_mini_skirt_black", "name": "Black Vinyl Mini Skirt", "brand": "VERA Mode", "desc": "A slick black vinyl mini skirt offering a wet-look shine and an ultra-short silhouette.", "image": "assets/content/clothing/bottoms/VERAMode/bottomVinylMiniSkirtBlack.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "mini", "baseLooks": 6, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_jacquard_shorts_gold", "name": "Gold Jacquard Shorts", "brand": "VERA Mode", "desc": "High-waisted elegant shorts tailored confidently from a rich gold floral jacquard fabric.", "image": "assets/content/clothing/bottoms/VERAMode/bottomJacquardShortsGold.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "shorts", "baseLooks": 8, "price": 50, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_zipper_skirt_leather", "name": "Leather Zipper Skirt", "brand": "VERA Mode", "desc": "A daring black leather mini skirt displaying a bold full-length silver zipper straight down the front.", "image": "assets/content/clothing/bottoms/VERAMode/bottomZipperSkirtLeather.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "mini", "baseLooks": 7, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 85, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_sheer_lace_pants_black", "name": "Sheer Lace Pants", "brand": "VERA Mode", "desc": "Translucent black floral lace trousers designed to be worn provocatively over a bodysuit or lingerie.", "image": "assets/content/clothing/bottoms/VERAMode/bottomSheerLacePantsBlack.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "straight", "baseLooks": 5, "price": 60, "quality": "Rare", "warmth": 0, "sexinessScore": 9, "exposureLevel": 9, "durability": 60, "reqCorruption": 6, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 75, "reqHeelsSkill": 0},
{"id": "bottom_satin_culottes_navy", "name": "Navy Satin Culottes", "brand": "VERA Mode", "desc": "Wide-leg glossy navy blue satin culottes dropping fluidly right to the mid-calf.", "image": "assets/content/clothing/bottoms/VERAMode/bottomSatinCulottesNavy.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "wide-leg", "baseLooks": 7, "price": 45, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 75, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_corset_waist_jeans", "name": "Corset Waist Jeans", "brand": "VERA Mode", "desc": "Dark wash jeans boasting an ultra-high corset waist tied snugly with intricate lacing detail.", "image": "assets/content/clothing/bottoms/VERAMode/bottomCorsetWaistJeans.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "straight", "baseLooks": 6, "price": 50, "quality": "Common", "warmth": 5, "sexinessScore": 4, "exposureLevel": 2, "durability": 90, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_fringe_mini_skirt_silver", "name": "Silver Fringe Mini Skirt", "brand": "VERA Mode", "desc": "A dazzling metallic silver mini skirt entirely draped in tiers of sparkling long fringe.", "image": "assets/content/clothing/bottoms/VERAMode/bottomFringeMiniSkirtSilver.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "mini", "baseLooks": 7, "price": 55, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_slit_joggers_grey", "name": "Grey Slit Joggers", "brand": "VERA Mode", "desc": "Athleisure grey jersey joggers dramatically split smartly down either side revealing the leg smoothly.", "image": "assets/content/clothing/bottoms/VERAMode/bottomSlitJoggersGrey.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 5, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["casual", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_velvet_leggings_burgundy", "name": "Burgundy Velvet Leggings", "brand": "VERA Mode", "desc": "Plush crushed burgundy velvet leggings fitting snugly safely with a luxurious texture.", "image": "assets/content/clothing/bottoms/VERAMode/bottomVelvetLeggingsBurgundy.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "leggings", "baseLooks": 6, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 2, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_cutout_mini_skirt_red", "name": "Red Cutout Mini Skirt", "brand": "VERA Mode", "desc": "A bold red mini skirt featuring sexy side cutouts and a tight fit.", "image": "assets/content/clothing/bottoms/VERAMode/bottomCutoutMiniSkirtRed.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "mini", "baseLooks": 6, "price": 45, "quality": "Common", "warmth": 0, "sexinessScore": 9, "exposureLevel": 8, "durability": 70, "reqCorruption": 5, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_ruched_pencil_skirt", "name": "Ruched Pencil Skirt", "brand": "VERA Mode", "desc": "A chic black pencil skirt gracefully gathered with side ruching to flatter the figure.", "image": "assets/content/clothing/bottoms/VERAMode/bottomRuchedPencilSkirt.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "pencil", "baseLooks": 6, "price": 50, "quality": "Common", "warmth": 1, "sexinessScore": 4, "exposureLevel": 2, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_flare_leggings_zebra", "name": "Zebra Flare Leggings", "brand": "VERA Mode", "desc": "Eye-catching black and white zebra print flared leggings made from highly stretchable curve-hugging fabric.", "image": "assets/content/clothing/bottoms/VERAMode/bottomFlareLeggingsZebra.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "leggings", "baseLooks": 6, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 2, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wrap_maxi_skirt_silk", "name": "Silk Wrap Maxi Skirt", "brand": "VERA Mode", "desc": "A luxurious emerald green silk wrap maxi skirt featuring a dangerously high leg slit for a dramatic finish.", "image": "assets/content/clothing/bottoms/VERAMode/bottomWrapMaxiSkirtSilk.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "maxi", "baseLooks": 8, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 8, "durability": 70, "reqCorruption": 4, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_sequined_shorts_black", "name": "Black Sequined Shorts", "brand": "VERA Mode", "desc": "Sparkling black sequin short shorts perfect for catching the light during a glamorous night out.", "image": "assets/content/clothing/bottoms/VERAMode/bottomSequinedShortsBlack.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "shorts", "baseLooks": 7, "price": 55, "quality": "Common", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_lace_up_pants_leather", "name": "Leather Lace-Up Pants", "brand": "VERA Mode", "desc": "Tight black synthetic leather pants featuring provocative side lace-up details running completely down the legs.", "image": "assets/content/clothing/bottoms/VERAMode/bottomLaceUpPantsLeather.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "straight", "baseLooks": 8, "price": 60, "quality": "Common", "warmth": 0, "sexinessScore": 9, "exposureLevel": 8, "durability": 80, "reqCorruption": 5, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_high_slit_skirt_white", "name": "White High-Slit Skirt", "brand": "VERA Mode", "desc": "A flowing summer-ready white maxi skirt punctuated with dramatic dual thigh-high slits.", "image": "assets/content/clothing/bottoms/VERAMode/bottomHighSlitSkirtWhite.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "maxi", "baseLooks": 6, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 7, "durability": 65, "reqCorruption": 4, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 25, "reqHeelsSkill": 0},
{"id": "bottom_pleated_skirt_leather", "name": "Leather Pleated Skirt", "brand": "VERA Mode", "desc": "A short black synthetic leather pleated skirt offering an edgy yet chic look.", "image": "assets/content/clothing/bottoms/VERAMode/bottomPleatedSkirtLeather.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "mini", "baseLooks": 7, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 85, "reqCorruption": 3, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_mesh_leggings_black", "name": "Black Mesh Leggings", "brand": "VERA Mode", "desc": "Athletic-inspired leggings constructed mainly from sheer black mesh revealing the legs, punctuated with solid spandex panels.", "image": "assets/content/clothing/bottoms/VERAMode/bottomMeshLeggingsBlack.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "leggings", "baseLooks": 6, "price": 35, "quality": "Common", "warmth": 0, "sexinessScore": 9, "exposureLevel": 9, "durability": 60, "reqCorruption": 6, "matchSet": null, "tags": ["sexy", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 70, "reqHeelsSkill": 0},
{"id": "bottom_cargo_pants_satin", "name": "Satin Cargo Pants", "brand": "VERA Mode", "desc": "A high-fashion take on cargo pants crafted from luxurious fluid champagne satin with large utility pockets.", "image": "assets/content/clothing/bottoms/VERAMode/bottomCargoPantsSatin.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "straight", "baseLooks": 8, "price": 90, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 1, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_micro_skirt_tartan", "name": "Tartan Micro Skirt", "brand": "VERA Mode", "desc": "An extremely short ultra-mini red tartan plaid micro skirt resting provocatively low on the hips.", "image": "assets/content/clothing/bottoms/VERAMode/bottomMicroSkirtTartan.webp", "store": "storeClothingB", "slot": "bottom", "silhouette": "mini", "baseLooks": 5, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 9, "exposureLevel": 8, "durability": 70, "reqCorruption": 5, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 85, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_tailored_trousers_black", "name": "Black Tailored Trousers", "brand": "Fifth Avenue", "desc": "Impeccably tailored high-waisted black wool trousers serving as a foundation for any chic ensemble.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomTailoredTrousersBlack.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "straight", "baseLooks": 7, "price": 140, "quality": "Epic", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_pleated_midi_skirt_camel", "name": "Camel Pleated Midi Skirt", "brand": "Fifth Avenue", "desc": "A timeless camel-colored pleated midi skirt crafted from luxurious heavy crepe fabric.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomPleatedMidiSkirtCamel.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "midi", "baseLooks": 7, "price": 120, "quality": "Epic", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 1, "matchSet": null, "tags": ["elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_silk_slip_skirt_champagne", "name": "Champagne Silk Slip Skirt", "brand": "Fifth Avenue", "desc": "An ultra-premium pure silk slip skirt that drapes effortlessly over the body forming a fluid silhouette.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomSilkSlipSkirtChampagne.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "midi", "baseLooks": 8, "price": 180, "quality": "Epic", "warmth": 0, "sexinessScore": 5, "exposureLevel": 2, "durability": 70, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wide_leg_pants_white", "name": "White Wide-Leg Pants", "brand": "Fifth Avenue", "desc": "Elegant white wide-leg trousers featuring front pleats and a draped, floor-skimming silhouette.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomWideLegPantsWhite.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "wide-leg", "baseLooks": 7, "price": 160, "quality": "Epic", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_cashmere_knit_skirt_grey", "name": "Grey Cashmere Knit Skirt", "brand": "Fifth Avenue", "desc": "A sumptuously soft grey cashmere midi skirt offering a refined and cozy fit.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomCashmereKnitSkirtGrey.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "midi", "baseLooks": 6, "price": 200, "quality": "Epic", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_leather_pencil_skirt_burgundy", "name": "Burgundy Leather Pencil Skirt", "brand": "Fifth Avenue", "desc": "A rich burgundy genuine leather pencil skirt, perfectly tailored for a commanding, sophisticated look.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomLeatherPencilSkirtBurgundy.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "pencil", "baseLooks": 7, "price": 250, "quality": "Legendary", "warmth": 2, "sexinessScore": 3, "exposureLevel": 2, "durability": 90, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_asymmetric_skirt_navy", "name": "Navy Asymmetric Skirt", "brand": "Fifth Avenue", "desc": "A conceptual navy blue high-fashion skirt featuring an elegant sweeping asymmetric hem.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomAsymmetricSkirtNavy.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "asymmetrical", "baseLooks": 7, "price": 175, "quality": "Epic", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_boucle_mini_skirt_cream", "name": "Cream Boucle Mini Skirt", "brand": "Fifth Avenue", "desc": "A classic cream boucle tweed mini skirt with delicate golden hardware detailing.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomBoucleMiniSkirtCream.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "mini", "baseLooks": 8, "price": 140, "quality": "Epic", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_tweed_trousers_grey", "name": "Grey Tweed Trousers", "brand": "Fifth Avenue", "desc": "Sophisticated high-waisted grey tweed trousers invoking a timeless heritage aesthetic.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomTweedTrousersGrey.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "straight", "baseLooks": 7, "price": 155, "quality": "Epic", "warmth": 4, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_satin_wrap_skirt_olive", "name": "Olive Satin Wrap Skirt", "brand": "Fifth Avenue", "desc": "A lustrous olive green satin wrap skirt that softly ties at the waist for a statuesque look.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomSatinWrapSkirtOlive.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "maxi", "baseLooks": 8, "price": 140, "quality": "Epic", "warmth": 0, "sexinessScore": 4, "exposureLevel": 2, "durability": 75, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_velvet_maxi_skirt_black", "name": "Black Velvet Maxi Skirt", "brand": "Fifth Avenue", "desc": "A show-stopping sweeping black velvet maxi skirt offering dramatic elegance and a rich texture.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomVelvetMaxiSkirtBlack.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "maxi", "baseLooks": 8, "price": 190, "quality": "Epic", "warmth": 3, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 1, "matchSet": null, "tags": ["elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_high_waist_flare_pants_camel", "name": "Camel Flare Pants", "brand": "Fifth Avenue", "desc": "Tailored high-waisted flares in a warm camel tone, designed to elongate the legs beautifully.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomHighWaistFlarePantsCamel.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "flared", "baseLooks": 7, "price": 165, "quality": "Epic", "warmth": 3, "sexinessScore": 4, "exposureLevel": 2, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_sequin_maxi_skirt_silver", "name": "Silver Sequin Maxi Skirt", "brand": "Fifth Avenue", "desc": "A floor-sweeping silver sequined maxi skirt guaranteed to turn heads at any gala.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomSequinMaxiSkirtSilver.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "maxi", "baseLooks": 9, "price": 280, "quality": "Legendary", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 65, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_cashmere_sweatpants_beige", "name": "Beige Cashmere Sweatpants", "brand": "Fifth Avenue", "desc": "Unbelievably soft pure cashmere lounge pants, redefining true luxury athleisure.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomCashmereSweatpantsBeige.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 4, "price": 60, "quality": "Epic", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wool_skirt_suit_navy", "name": "Navy Wool Skirt Suit Bottom", "brand": "Fifth Avenue", "desc": "A sophisticated navy wool straight skirt, typically part of a sharp power suit.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomWoolSkirtSuitNavy.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "straight", "baseLooks": 7, "price": 130, "quality": "Epic", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_silk_cargo_pants_black", "name": "Black Silk Cargo Pants", "brand": "Fifth Avenue", "desc": "A purely high-fashion approach to cargo trousers made entirely of lustrous black silk.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomSilkCargoPantsBlack.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 7, "price": 195, "quality": "Epic", "warmth": 0, "sexinessScore": 4, "exposureLevel": 1, "durability": 75, "reqCorruption": 2, "matchSet": null, "tags": ["elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_pleated_culottes_cream", "name": "Cream Pleated Culottes", "brand": "Fifth Avenue", "desc": "Flowing cream-colored pleated culottes offering the sheer elegance of a midi skirt with the comfort of pants.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomPleatedCulottesCream.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "wide-leg", "baseLooks": 6, "price": 135, "quality": "Epic", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_pencil_skirt_lace_black", "name": "Black Lace Pencil Skirt", "brand": "Fifth Avenue", "desc": "An exquisite black guipure lace pencil skirt layered delicately over a nude silk lining.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomPencilSkirtLaceBlack.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "pencil", "baseLooks": 8, "price": 170, "quality": "Epic", "warmth": 1, "sexinessScore": 6, "exposureLevel": 7, "durability": 70, "reqCorruption": 3, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_high_waist_shorts_linen", "name": "Linen High-Waist Shorts", "brand": "Fifth Avenue", "desc": "Impeccably tailored white linen shorts offering breezy sophistication for luxury resort wear.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomHighWaistShortsLinen.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "shorts", "baseLooks": 6, "price": 110, "quality": "Epic", "warmth": 0, "sexinessScore": 2, "exposureLevel": 1, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_tweed_mini_skirt_pink", "name": "Pink Tweed Mini Skirt", "brand": "Fifth Avenue", "desc": "A modern, playful pink tweed mini skirt woven intricately with luxurious subtle metallic threads.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomTweedMiniSkirtPink.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "mini", "baseLooks": 6, "price": 160, "quality": "Epic", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 90, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wrap_trousers_olive", "name": "Olive Wrap Trousers", "brand": "Fifth Avenue", "desc": "Unique olive draping trousers featuring a chic asymmetrical front tie wrap securely sitting at the waist.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomWrapTrousersOlive.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "straight", "baseLooks": 8, "price": 135, "quality": "Epic", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_crocheted_maxi_skirt_white", "name": "White Crocheted Maxi Skirt", "brand": "Fifth Avenue", "desc": "An artisan-crafted white crocheted maxi skirt displaying sheer elegance, perfect for high-end coastal respites.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomCrochetedMaxiSkirtWhite.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "maxi", "baseLooks": 7, "price": 200, "quality": "Epic", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "matchSet": null, "tags": ["elegant", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "bottom_leather_leggings_black", "name": "Black Leather Leggings", "brand": "Fifth Avenue", "desc": "Ultra-premium stretch lambskin leather leggings providing an unparalleled sleek fit.", "image": "assets/content/clothing/bottoms/FifthAvenue/bottomLeatherLeggingsBlack.webp", "store": "storeClothingC", "slot": "bottom", "silhouette": "leggings", "baseLooks": 6, "price": 250, "quality": "Legendary", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 85, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_yoga_pants_black", "name": "Black Yoga Pants", "brand": "FastBreak Athletics", "desc": "Classic tight black yoga pants with a slight flare at the hem, an essential workout basic.", "image": "assets/content/clothing/bottoms/FastBreak/bottomYogaPantsBlack.webp", "store": "storeSports", "slot": "bottom", "silhouette": "leggings", "baseLooks": 4, "price": 35, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 2, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_gym_shorts_grey", "name": "Grey Gym Shorts", "brand": "FastBreak Athletics", "desc": "Breathable grey athletic shorts featuring a comfortable elastic waistband for unrestricted movement.", "image": "assets/content/clothing/bottoms/FastBreak/bottomGymShortsGrey.webp", "store": "storeSports", "slot": "bottom", "silhouette": "shorts", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 1, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_running_tights_blue", "name": "Blue Running Tights", "brand": "FastBreak Athletics", "desc": "Performance-enhancing compression running tights in a vibrant electric blue.", "image": "assets/content/clothing/bottoms/FastBreak/bottomRunningTightsBlue.webp", "store": "storeSports", "slot": "bottom", "silhouette": "leggings", "baseLooks": 4, "price": 45, "quality": "Uncommon", "warmth": 0, "sexinessScore": 3, "exposureLevel": 2, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_sweatpants_oversized_grey", "name": "Oversized Grey Sweatpants", "brand": "FastBreak Athletics", "desc": "Thick and cozy oversized heather grey sweatpants perfect for recovering after an intense workout.", "image": "assets/content/clothing/bottoms/FastBreak/bottomSweatpantsOversizedGrey.webp", "store": "storeSports", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 3, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_biker_shorts_black_sports", "name": "Black Biker Shorts", "brand": "FastBreak Athletics", "desc": "Sleek and highly stretchy black biker shorts hugging the thighs perfectly for high endurance sports.", "image": "assets/content/clothing/bottoms/FastBreak/bottomBikerShortsBlack.webp", "store": "storeSports", "slot": "bottom", "silhouette": "shorts", "baseLooks": 4, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_track_pants_navy", "name": "Navy Track Pants", "brand": "FastBreak Athletics", "desc": "Classic sporty navy track pants adorned with distinct double white side stripes.", "image": "assets/content/clothing/bottoms/FastBreak/bottomTrackPantsNavy.webp", "store": "storeSports", "slot": "bottom", "silhouette": "straight", "baseLooks": 3, "price": 40, "quality": "Uncommon", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_seamless_leggings_pink", "name": "Pink Seamless Leggings", "brand": "FastBreak Athletics", "desc": "Bright pink seamless gym leggings featuring clever contouring stitch details highlighting the physique.", "image": "assets/content/clothing/bottoms/FastBreak/bottomSeamlessLeggingsPink.webp", "store": "storeSports", "slot": "bottom", "silhouette": "leggings", "baseLooks": 4, "price": 45, "quality": "Uncommon", "warmth": 0, "sexinessScore": 4, "exposureLevel": 2, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sexy", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_tennis_skirt_white", "name": "White Tennis Skirt", "brand": "FastBreak Athletics", "desc": "A crisp white pleated tennis skirt featuring built-in hidden shorts.", "image": "assets/content/clothing/bottoms/FastBreak/bottomTennisSkirtWhite.webp", "store": "storeSports", "slot": "bottom", "silhouette": "mini", "baseLooks": 5, "price": 40, "quality": "Uncommon", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_scrunch_shorts_peach", "name": "Peach Scrunch Shorts", "brand": "FastBreak Athletics", "desc": "Trendy peach-colored gym shorts featuring a flattering scrunch back detail highlighting the curves.", "image": "assets/content/clothing/bottoms/FastBreak/bottomScrunchShortsPeach.webp", "store": "storeSports", "slot": "bottom", "silhouette": "shorts", "baseLooks": 4, "price": 35, "quality": "Uncommon", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 85, "reqCorruption": 5, "matchSet": null, "tags": ["casual", "sexy", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_cargo_joggers_khaki", "name": "Khaki Cargo Joggers", "brand": "FastBreak Athletics", "desc": "Sporty khaki cargo joggers tapering down gracefully to tightly elasticated ankle cuffs.", "image": "assets/content/clothing/bottoms/FastBreak/bottomCargoJoggersKhaki.webp", "store": "storeSports", "slot": "bottom", "silhouette": "relaxed", "baseLooks": 4, "price": 45, "quality": "Uncommon", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_wrap_athletic_skirt_black", "name": "Black Athletic Wrap Skirt", "brand": "FastBreak Athletics", "desc": "A sporty black wrap-style skort combining the look of a skirt with built-in compression shorts.", "image": "assets/content/clothing/bottoms/FastBreak/bottomWrapAthleticSkirtBlack.webp", "store": "storeSports", "slot": "bottom", "silhouette": "mini", "baseLooks": 4, "price": 40, "quality": "Uncommon", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_sweatshorts_blue", "name": "Blue Sweatshorts", "brand": "FastBreak Athletics", "desc": "Ultra-comfortable vintage blue cotton sweatshorts featuring an adjustable flat drawstring.", "image": "assets/content/clothing/bottoms/FastBreak/bottomSweatshortsBlue.webp", "store": "storeSports", "slot": "bottom", "silhouette": "shorts", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_yoga_flare_pants_grey", "name": "Grey Yoga Flares", "brand": "FastBreak Athletics", "desc": "Flattering heather grey yoga pants holding tightly securely down the thighs with a dramatic wide flare from the knee down.", "image": "assets/content/clothing/bottoms/FastBreak/bottomYogaFlarePantsGrey.webp", "store": "storeSports", "slot": "bottom", "silhouette": "flared", "baseLooks": 5, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 1, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_mesh_panel_leggings_black", "name": "Mesh Panel Leggings", "brand": "FastBreak Athletics", "desc": "Black high-performance leggings featuring cooling sheer mesh panels slicing down the calves.", "image": "assets/content/clothing/bottoms/FastBreak/bottomMeshPanelLeggingsBlack.webp", "store": "storeSports", "slot": "bottom", "silhouette": "leggings", "baseLooks": 5, "price": 45, "quality": "Uncommon", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 75, "reqCorruption": 3, "matchSet": null, "tags": ["casual", "sexy", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottom_retro_running_shorts_red", "name": "Red Retro Shorts", "brand": "FastBreak Athletics", "desc": "Classic bright red 80s-inspired curved-hem running shorts bordered cleanly by bright white piping.", "image": "assets/content/clothing/bottoms/FastBreak/bottomRetroRunningShortsRed.webp", "store": "storeSports", "slot": "bottom", "silhouette": "shorts", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 85, "reqCorruption": 4, "matchSet": null, "tags": ["casual", "cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bottomRubysDishwash", "name": "Ruby's Diner Dishwasher Pants", "brand": "", "desc": "Heavy cotton work pants issued for dishwasher shifts at Ruby's Diner. Straight leg, durable.", "image": "assets/content/clothing/others/RubysDiner/bottomRubysDishwash.webp", "store": "", "slot": "bottom", "silhouette": "straight", "baseLooks": 1, "price": 0, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["professional"], "npcAppeal": {}, "shopAvailable": false, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* bracelets DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.bracelets = [
{"id": "jewel_br_tennis_crystal_silver_t4", "name": "Classic Tennis Bracelet", "brand": "Diamond Dreams", "desc": "The ultimate status symbol. A perfectly flexible, symmetrical, straight row of pristine clear cubic zirconia stones set deeply into a white gold chain.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRTennisCrystalSilverT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 9, "price": 400, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_chunky_chain_gold_t3", "name": "Thick Gold Curb Chain", "brand": "Diamond Dreams", "desc": "A massively bold statement link bracelet cast in heavy, solid polished yellow gold. Features oversized interlocking rounded curb links.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRChunkyChainGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 7, "price": 405, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_bangle_set_stacked_silver_t2", "name": "Silver Bangle Stacks", "brand": "Diamond Dreams", "desc": "A set of five distinct thin, solid, unbending sterling silver bangle hoops. Designed to stack loosely and slide harmoniously down the wrist.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRBangleSetStackedSilverT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 6, "price": 355, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_cuff_minimal_rose_gold_t3", "name": "Minimalist Rose Cuff", "brand": "Diamond Dreams", "desc": "An aggressively modern, impeccably smooth C-shaped solid metal open cuff. Crafted from polished, warm rose gold with intentionally blunt square ends.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRCuffMinimalRoseGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 7, "price": 420, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_charm_flora_silver_t2", "name": "Silver Floral Charm Link", "brand": "Diamond Dreams", "desc": "A classic fine link chain bracelet heavily decorated with multiple tiny swinging silver charms shaped like daisies, roses, and little leaves.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRCharmFloraSilverT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 6, "price": 310, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_pearl_stretch_cream_t2", "name": "Cream Pearl Stretch Band", "brand": "Diamond Dreams", "desc": "Traditional, unassuming, and extremely easy to wear. Completely round, matching cream freshwater pearls tightly threaded on a thick invisible elastic band.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRPearlStretchCreamT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 6, "price": 270, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_paperclip_link_gold_t3", "name": "Paperclip Link Gold", "brand": "Diamond Dreams", "desc": "A trendy, ultra-lightweight chain defined by starkly elongated, geometric rectangular loops affectionately called 'paperclip' links. Crafted in hollow gold.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRPaperclipLinkGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 7, "price": 365, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_mesh_crystal_knot_silver_t4", "name": "Crystal Mesh Knot Cuff", "brand": "Diamond Dreams", "desc": "A dazzlingly wide, flexible silver metallic mesh ribbon, thickly paved with micro-crystals, intentionally tied at the center mimicking an elegant sloppy fabric knot.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRMeshCrystalKnotSilverT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 8, "price": 370, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_lever_hinge_diamond_gold_t4", "name": "Hinged Halo Gold Bangle", "brand": "Diamond Dreams", "desc": "An aggressively structured, perfectly oval 18k solid gold hinged bracelet. The central bar features three massive inset brilliant cubic zirconia stones.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRLeverHingeDiamondGoldT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 9, "price": 465, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["elegant"], "npcAppeal": {"elite": 3}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_ledger_id_tag_silver_t2", "name": "Silver ID Tag Bar", "brand": "Diamond Dreams", "desc": "A plain, somewhat masculine flat shiny silver rectangular ID bar resting tightly between two lengths of solid motorcycle-style curb chain.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRLedgerIDTagSilverT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 6, "price": 365, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_br_macrame_pull_cord_rose_t2", "name": "Rose Gold Tassel Cord", "brand": "Diamond Dreams", "desc": "A delicate, highly adjustable casual piece consisting of a tiny rose gold pendant threaded onto a tough blush-colored braided nylon pull-cord slipping securely over the hand.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelBRMacramePullCordRoseT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "bracelet", "baseLooks": 6, "price": 290, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* Bras DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.bras = [
{"id": "bra_sport_white_owned", "name": "White Everyday Bra", "brand": "", "desc": "Simple white everyday bra.", "image": "assets/content/clothing/bra/Starter/bra_sport_white_owned.webp", "store": "", "slot": "bra", "silhouette": "basic bra", "baseLooks": 1, "price": 0, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": false, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_black_owned", "name": "Black Everyday Bra", "brand": "", "desc": "Simple black everyday bra.", "image": "assets/content/clothing/bra/Starter/bra_black_owned.webp", "store": "", "slot": "bra", "silhouette": "basic bra", "baseLooks": 1, "price": 0, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": false, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_basic_cotton_white", "name": "White Cotton Everyday T-Shirt Bra", "brand": "Northline Apparel", "desc": "Smooth white cotton-blend T-shirt bra with seamless moulded cups, wide underband, and adjustable straps. Everyday essential.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthBasicCottonWhite.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "push-up", "baseLooks": 1, "price": 10, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_basic_cotton_nude", "name": "Nude Cotton Everyday T-Shirt Bra", "brand": "Northline Apparel", "desc": "Seamless nude cotton-blend T-shirt bra with moulded cups and wide adjustable straps. Invisible under clothing.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthBasicCottonNude.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "push-up", "baseLooks": 2, "price": 10, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_sports_mesh_grey", "name": "Heather Grey Medium-Support Sports Bra", "brand": "Northline Apparel", "desc": "Heather grey medium-support sports bra with mesh racerback panel, wide underband, and flat-lock seams for comfort during activity.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthSportsMeshGrey.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 1, "price": 10, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_sports_mesh_navy", "name": "Navy Medium-Support Sports Bra", "brand": "Northline Apparel", "desc": "Navy blue medium-support sports bra with mesh ventilation panels on sides and back, flat seams and adjustable straps.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthSportsMeshNavy.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 2, "price": 10, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_bralette_rib_black", "name": "Black Ribbed Bralette", "brand": "Northline Apparel", "desc": "Black ribbed stretch-cotton bralette with a scoop neckline, wide underband, and minimal back clasp. Casual and comfortable.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthBraletteRibBlack.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "bralette", "baseLooks": 3, "price": 15, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_bralette_rib_olive", "name": "Olive Ribbed Bralette", "brand": "Northline Apparel", "desc": "Dusty olive ribbed stretch-cotton bralette with a scoop neckline and wide flat underband. Earthy and effortless.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthBraletteRibOlive.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "bralette", "baseLooks": 4, "price": 15, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_bralette_lace_ecru", "name": "Ecru Lace-Trim Bralette", "brand": "Northline Apparel", "desc": "Soft ecru cotton bralette with delicate lace trim along the upper edge and straps, giving a feminine touch to a casual silhouette.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthBraletteLaceEcru.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "bralette", "baseLooks": 5, "price": 15, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_high_impact_sports_crop_black", "name": "Black High-Impact Zip-Front Sports Bra", "brand": "Northline Apparel", "desc": "Black high-impact sports bra with front zip closure, wide Y-back straps, and bonded underband for maximum support.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthHighImpactSportsCropBlack.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 3, "price": 15, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_bralette_padded_dusty_rose", "name": "Dusty Rose Padded Lace Bralette", "brand": "Northline Apparel", "desc": "Dusty rose padded lace bralette with delicate all-over lace fabric, removable padding, and dainty adjustable straps.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthBralettePaddedDustyRose.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "bralette", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 1, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_north_bralette_cutout_sage", "name": "Sage Ribbed Cut-Out Bralette", "brand": "Northline Apparel", "desc": "Sage green ribbed bralette with a front cut-out detail at centre chest and criss-cross strap detail at back.", "image": "assets/content/clothing/bra/NorthlineApparel/braNorthBraletteCutoutSage.webp", "store": "storeClothingA", "slot": "bra", "silhouette": "bralette", "baseLooks": 5, "price": 20, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 1, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_basic_cotton_blush_bralette", "name": "Blush Cotton Soft-Cup Bralette", "brand": "Silk & Lace", "desc": "Soft blush pink cotton bralette with a subtle lace trim at the upper edge and thin adjustable straps. A gentle everyday lingerie piece.", "image": "assets/content/clothing/bra/SilkAndLace/braSLBasicCottonBlushBralette.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "bralette", "baseLooks": 3, "price": 20, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_satin_bandeau_ivory", "name": "Ivory Satin Bandeau Bra", "brand": "Silk & Lace", "desc": "Ivory satin strapless bandeau bra with a wide elasticated band, subtle satin sheen, and removable straps for versatility.", "image": "assets/content/clothing/bra/SilkAndLace/braSLSatinBandeauIvory.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "bandeau", "baseLooks": 3, "price": 20, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_lace_balconette_dusty_mauve", "name": "Dusty Mauve Lace Balconette Bra", "brand": "Silk & Lace", "desc": "Dusty mauve all-over lace balconette bra with scalloped edge cups, underwire support and adjustable straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLLaceBalconetteDustyMailve.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "balconette", "baseLooks": 4, "price": 25, "quality": "Rare", "warmth": 1, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_satin_plunge_navy", "name": "Navy Satin Plunge Bra", "brand": "Silk & Lace", "desc": "Deep navy satin plunge bra with a low-cut centre front, lace-trim underwire cups and matching satin straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLSatinPlungeNavy.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "plunge", "baseLooks": 4, "price": 30, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_ribbon_satin_blush_set", "name": "Dusty Blush Satin Ribbon-Tie Bralette", "brand": "Silk & Lace", "desc": "Soft dusty blush satin bralette with a delicate ribbon-tie front detail and lace-trim band. Part of the silkBlushRibbonSet.", "image": "assets/content/clothing/bra/SilkAndLace/braSLRibbonSatinBlushSet.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "bralette", "baseLooks": 4, "price": 30, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 2, "matchSet": "silkBlushRibbonSet", "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_lace_bralette_cream_floral", "name": "Cream Floral Lace Bralette", "brand": "Silk & Lace", "desc": "Cream all-over floral lace bralette with a delicate scalloped hem, thin straps and a clasp back. Romantic and feminine.", "image": "assets/content/clothing/bra/SilkAndLace/braSLLaceBraletteCreamFloral.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "bralette", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 1, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_push_up_satin_black", "name": "Black Satin Push-Up Bra", "brand": "Silk & Lace", "desc": "Deep black satin push-up bra with angled padding, underwire cups, a lace-trim upper edge and delicate satin-covered straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLPushUpSatinBlack.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "push-up", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 1, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_plunge_emerald_lace_strappy", "name": "Emerald Lace Strappy Plunge Bra", "brand": "Silk & Lace", "desc": "Rich emerald green all-over lace plunge bra with adjustable strappy detail across the cups and gold ring hardware accent.", "image": "assets/content/clothing/bra/SilkAndLace/braSLPlungeEmeraldLaceStrappy.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "plunge", "baseLooks": 5, "price": 35, "quality": "Rare", "warmth": 1, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 3, "matchSet": "silkEmeraldStrappySet", "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_sheer_mesh_burgundy_balconette", "name": "Burgundy Sheer Mesh Balconette Bra", "brand": "Silk & Lace", "desc": "Deep burgundy sheer mesh balconette bra with lace-trim upper cup edge, underwire and adjustable jewel-tone straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLSheerMeshBurgundyBalconette.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "balconette", "baseLooks": 5, "price": 35, "quality": "Rare", "warmth": 1, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_premium_satin_rose_gold_plunge", "name": "Rose Gold Satin Deep-Plunge Bra", "brand": "Silk & Lace", "desc": "Luxurious rose gold satin deep-plunge bra with delicate crystal-charm centre detail, lace-trim cups and satin-covered thin straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLPremiumSatinRoseGoldPlunge.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "plunge", "baseLooks": 6, "price": 45, "quality": "Premium", "warmth": 1, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_cotton_soft_cup_white", "name": "White Cotton Soft-Cup Triangle Bra", "brand": "Silk & Lace", "desc": "Clean white cotton soft-cup triangle bra with thin adjustable straps and a minimal clasp back. Lightweight everyday lingerie.", "image": "assets/content/clothing/bra/SilkAndLace/braSLCottonSoftCupWhite.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "bralette", "baseLooks": 3, "price": 20, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_lace_bralette_lavender", "name": "Lavender Lace Unlined Bralette", "brand": "Silk & Lace", "desc": "Soft lavender all-over lace unlined bralette with a delicate scalloped edge, thin straps and a dainty back clasp.", "image": "assets/content/clothing/bra/SilkAndLace/braSLLaceBraletteLavender.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "bralette", "baseLooks": 4, "price": 25, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_satin_balconette_coral_set", "name": "Coral Satin Scallop Balconette Bra", "brand": "Silk & Lace", "desc": "Coral satin balconette bra with wide scalloped lace trim along the upper cup edge and a small satin bow at centre front. Part of the silkCoralFloralSet.", "image": "assets/content/clothing/bra/SilkAndLace/braSLSatinBalconetteCoralSet.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "balconette", "baseLooks": 4, "price": 30, "quality": "Rare", "warmth": 1, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": "silkCoralFloralSet", "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_satin_plunge_forest_green", "name": "Forest Green Satin Plunge Bra", "brand": "Silk & Lace", "desc": "Rich forest green satin plunge bra with a deep centre-front V, lace-trim underwire cups and slender satin straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLSatinPlungeForestGreen.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "plunge", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_sheer_mesh_peach_balconette", "name": "Peach Sheer Mesh Balconette Bra", "brand": "Silk & Lace", "desc": "Soft peach sheer mesh balconette bra with a scalloped lace upper cup edge, underwire support and matching peach straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLSheerMeshPeachBalconette.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "balconette", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_premium_lace_plunge_wine_red", "name": "Wine Red Guipure Lace Plunge Bra", "brand": "Silk & Lace", "desc": "Wine red guipure lace plunge bra with structured underwire cups, deep centre plunge and delicate satin-bow shoulder straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLPremiumLacePlungeWineRed.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "plunge", "baseLooks": 6, "price": 45, "quality": "Premium", "warmth": 1, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_premium_sheer_embroidered_charcoal", "name": "Charcoal Sheer Embroidered Balconette Bra", "brand": "Silk & Lace", "desc": "Dark charcoal sheer tulle balconette bra with hand-embroidered floral motifs across the cups, satin underwire and delicate straps.", "image": "assets/content/clothing/bra/SilkAndLace/braSLPremiumSheerEmbroideredCharcoal.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "balconette", "baseLooks": 7, "price": 55, "quality": "Rare", "warmth": 1, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {"elite": 8}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_premium_satin_strapless_nude", "name": "Nude Satin Structured Strapless Bra", "brand": "Silk & Lace", "desc": "Smooth nude satin structured strapless bra with reinforced boning, silicone grip band and removable straps. Premium everyday luxury.", "image": "assets/content/clothing/bra/SilkAndLace/braSLPremiumSatinStraplessNude.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "bandeau", "baseLooks": 6, "price": 50, "quality": "Premium", "warmth": 1, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {"professional": 5, "elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_sl_premium_lace_halter_teal", "name": "Teal Lace Halter Plunge Bra", "brand": "Silk & Lace", "desc": "Deep teal all-over lace halter plunge bra with a convertible halter neck strap, underwire and strappy centre-front detail.", "image": "assets/content/clothing/bra/SilkAndLace/braSLPremiumLaceHalterTeal.webp", "store": "storeLingerieA", "slot": "bra", "silhouette": "plunge", "baseLooks": 7, "price": 55, "quality": "Rare", "warmth": 1, "sexinessScore": 8, "exposureLevel": 8, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {"elite": 10}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_lace_balconette_purple_t3", "name": "Deep Violet Lace Balconette Bra", "brand": "Intimate Secrets", "desc": "Deep violet all-over lace balconette bra with underwire, scalloped cup edge and adjustable straps. An entry into Intimate Secrets' signature daring aesthetic.", "image": "assets/content/clothing/bra/IntimateSecrets/braISLaceBalconettePurpleT3.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "balconette", "baseLooks": 4, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 3, "matchSet": "intimateRedLaceSet", "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_sheer_mesh_plunge_red_t3", "name": "Crimson Sheer Mesh Plunge Bra", "brand": "Intimate Secrets", "desc": "Crimson red sheer mesh plunge bra with lace-trim underwire cups, deep centre-front V and adjustable satin-finish straps.", "image": "assets/content/clothing/bra/IntimateSecrets/braISSheerMeshPlungeRedT3.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "plunge", "baseLooks": 4, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["sexy", "daring", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_strappy_cage_black_mesh_t4", "name": "Deep Matte Black Strappy Cage Mesh Bra", "brand": "Intimate Secrets", "desc": "Deep matte black sheer mesh balconette bra with adjustable strappy cage detail across the cups and matte O-ring hardware. Part of the intimateBlackMeshSet.", "image": "assets/content/clothing/bra/IntimateSecrets/braISStrappyCageBlackMeshT4.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "balconette", "baseLooks": 5, "price": 60, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 5, "matchSet": "intimateBlackMeshSet", "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_sheer_lace_plunge_night_blue_t4", "name": "Midnight Navy Sheer Lace Plunge Bra", "brand": "Intimate Secrets", "desc": "Midnight navy sheer lace plunge bra with a barely-there deep V, strappy detail at the cups and delicate O-ring hardware at centre.", "image": "assets/content/clothing/bra/IntimateSecrets/braISSheerLacePlungeNightBlueT4.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "plunge", "baseLooks": 5, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_harness_strap_black_satin_t4", "name": "Black Satin Harness-Strap Bra", "brand": "Intimate Secrets", "desc": "Black satin bra with an elaborate harness-style strap construction across the cups, chest and back, with matte ring hardware throughout.", "image": "assets/content/clothing/bra/IntimateSecrets/braISHarnessStrapBlackSatinT4.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "balconette", "baseLooks": 5, "price": 70, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_snake_print_mesh_plunge_t4", "name": "Snake Print Mesh Plunge Bra", "brand": "Intimate Secrets", "desc": "Tonal grey snake print sheer mesh plunge bra with strappy cup detail, underwire and gold hardware accents. Part of the intimateSnakeMeshSet.", "image": "assets/content/clothing/bra/IntimateSecrets/braISSnakePrintMeshPlungeT4.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "plunge", "baseLooks": 5, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 5, "matchSet": "intimateSnakeMeshSet", "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_barely_there_triangle_red_t5", "name": "Red Barely-There Triangle Micro Bra", "brand": "Intimate Secrets", "desc": "Deep red minimal triangle micro bra with thin string straps, tiny triangular cups and a barely-there silhouette. Maximum exposure.", "image": "assets/content/clothing/bra/IntimateSecrets/braISBarelyThereTriangleRedT5.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "bralette", "baseLooks": 5, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 9, "exposureLevel": 9, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_open_cup_lace_black_t5", "name": "Black Lace Open-Cup Shelf Bra", "brand": "Intimate Secrets", "desc": "Matte black all-over lace shelf bra with open underwire cups that frame without covering, a wide elasticated band and adjustable straps.", "image": "assets/content/clothing/bra/IntimateSecrets/braISOpenCupLaceBlackT5.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "balconette", "baseLooks": 5, "price": 80, "quality": "Rare", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "lewd", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 95, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_premium_wet_look_plunge_black_t4", "name": "Black Wet-Look Deep Plunge Bra", "brand": "Intimate Secrets", "desc": "Glossy black wet-look faux leather deep plunge bra with underwire, strappy centre-front detail and adjustable glossy straps.", "image": "assets/content/clothing/bra/IntimateSecrets/braISPremiumWetLookPlungeBlackT4.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "plunge", "baseLooks": 6, "price": 85, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 85, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_premium_sheer_lace_red_harness_t5", "name": "Red Sheer Lace Harness Underwire Bra", "brand": "Intimate Secrets", "desc": "Deep red sheer lace underwire bra with an elaborate harness-strap overlay construction, gold O-ring hardware and adjustable straps.", "image": "assets/content/clothing/bra/IntimateSecrets/braISPremiumSheerLaceRedHarnessT5.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "balconette", "baseLooks": 6, "price": 95, "quality": "Premium", "warmth": 0, "sexinessScore": 9, "exposureLevel": 9, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_cut_out_mesh_lavender_t4", "name": "Lavender Cut-Out Mesh Balconette Bra", "brand": "Intimate Secrets", "desc": "Soft lavender sheer mesh balconette bra with geometric cut-out panels across the cups, thin straps and silver ring hardware.", "image": "assets/content/clothing/bra/IntimateSecrets/braISCutOutMeshLavenderT4.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "balconette", "baseLooks": 5, "price": 60, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_body_chain_gold_black_t5", "name": "Black Lace Bra with Gold Chain Chest Harness", "brand": "Intimate Secrets", "desc": "Barely-there black lace bra cups with a delicate gold chain harness across the bust onlythin chains between and over the cups, no chain below the chest.", "image": "assets/content/clothing/bra/IntimateSecrets/braISBodyChainGoldBlackT5.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "bralette", "baseLooks": 5, "price": 90, "quality": "Rare", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "lewd", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 95, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_premium_leather_look_red_t5", "name": "Red Faux-Leather Strappy Micro Bra", "brand": "Intimate Secrets", "desc": "Deep red faux-leather micro bra with minimal triangular cups, elaborate criss-cross strap front construction and silver hardware.", "image": "assets/content/clothing/bra/IntimateSecrets/braISPremiumLeatherLookRedT5.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "bralette", "baseLooks": 6, "price": 95, "quality": "Premium", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "lewd", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 100, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_premium_floral_embroidered_ecru_t3", "name": "Ecru Floral Embroidered Balconette Bra", "brand": "Intimate Secrets", "desc": "Ecru sheer tulle balconette bra with raised 3D floral embroidery across the cups, underwire support and satin-ribbon straps.", "image": "assets/content/clothing/bra/IntimateSecrets/braISPremiumFloralEmbroideredEcruT3.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "balconette", "baseLooks": 7, "price": 90, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "daring", "revealing"], "npcAppeal": {"elite": 8}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_is_premium_crystal_chain_black_t5", "name": "Black Lace Crystal-Chain Embellished Bra", "brand": "Intimate Secrets", "desc": "Barely-there black lace micro bra with dangling crystal chain embellishments hanging from the cup edges and straps. Maximum luxury, maximum exposure.", "image": "assets/content/clothing/bra/IntimateSecrets/braISPremiumCrystalChainBlackT5.webp", "store": "storeLingerieB", "slot": "bra", "silhouette": "bralette", "baseLooks": 7, "price": 100, "quality": "Premium", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "lewd", "sexy"], "npcAppeal": {"elite": 10}, "shopAvailable": true, "startOwned": false, "reqConfidence": 100, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_fb_sports_low_impact_white", "name": "White Low-Impact Seamless Sports Bra", "brand": "FastBreak", "desc": "White seamless low-impact sports bra with a stretchy scoop neckline, soft flat seams and light support. Great for yoga and casual wear.", "image": "assets/content/clothing/bra/FastBreak/braFBSportsLowImpactWhite.webp", "store": "storeSports", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 1, "price": 10, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_fb_sports_medium_black", "name": "Black Medium-Support Racerback Sports Bra", "brand": "FastBreak", "desc": "Black medium-support racerback sports bra with wide underband, removable cups, and moisture-wicking fabric.", "image": "assets/content/clothing/bra/FastBreak/braFBSportsMediumBlack.webp", "store": "storeSports", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 2, "price": 10, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_fb_sports_medium_navy", "name": "Navy Medium-Support Racerback Sports Bra", "brand": "FastBreak", "desc": "Navy blue medium-support racerback sports bra with ventilated back panel and wide bonded underband.", "image": "assets/content/clothing/bra/FastBreak/braFBSportsMediumNavy.webp", "store": "storeSports", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 2, "price": 10, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_fb_sports_high_crop_charcoal", "name": "Charcoal High-Neck Crop Sports Bra", "brand": "FastBreak", "desc": "Charcoal high-neck crop sports bra with a supportive thick strap back, wide underband and smooth compressive fabric.", "image": "assets/content/clothing/bra/FastBreak/braFBSportsHighCropCharcoal.webp", "store": "storeSports", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 3, "price": 15, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_fb_sports_strap_detail_olive", "name": "Olive Multi-Strap Sports Bra", "brand": "FastBreak", "desc": "Olive green medium-support sports bra with decorative multi-strap back panel, wide underband and stretch-fit fabric.", "image": "assets/content/clothing/bra/FastBreak/braFBSportsStrapDetailOlive.webp", "store": "storeSports", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 4, "price": 15, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sporty", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_fb_sports_mesh_panel_coral", "name": "Coral Mesh-Insert High-Support Sports Bra", "brand": "FastBreak", "desc": "Coral high-support sports bra with wide-band underband, sheer mesh front insert for breathability and thick adjustable straps.", "image": "assets/content/clothing/bra/FastBreak/braFBSportsMeshPanelCoral.webp", "store": "storeSports", "slot": "bra", "silhouette": "sports-bra", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "sporty", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "bra_fb_sports_bralette_tie_dye", "name": "Tie-Dye Longline Sports Bralette", "brand": "FastBreak", "desc": "Tie-dye purple-pink longline sports bralette with a wide ribbed underband, thin adjustable straps and light padding.", "image": "assets/content/clothing/bra/FastBreak/braFBSportsBraletteTieDye.webp", "store": "storeSports", "slot": "bra", "silhouette": "bralette", "baseLooks": 5, "price": 20, "quality": "Rare", "warmth": 1, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* coats DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.coats = [
{"id": "coat_north_classic_denim", "name": "Classic Blue Denim Jacket", "brand": "Northline", "desc": "A timeless medium wash blue denim jacket. Rugged and durable for everyday casual wear.", "image": "assets/content/clothing/coats/Northline/coatNorthClassicDenim.webp", "store": "storeClothingA", "slot": "coat", "silhouette": "denim-jacket", "baseLooks": 5, "price": 60, "quality": "Standard", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "outdoor"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_north_black_puffer", "name": "Black Insulated Puffer", "brand": "Northline", "desc": "A thick, heavily insulated black puffer jacket. Guaranteed to keep the chill out during deep winter.", "image": "assets/content/clothing/coats/Northline/coatNorthBlackPuffer.webp", "store": "storeClothingA", "slot": "coat", "silhouette": "puffer", "baseLooks": 4, "price": 85, "quality": "Standard", "warmth": 4, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "outdoor", "winter"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_north_olive_parka", "name": "Olive Green Utility Parka", "brand": "Northline", "desc": "A heavy olive green parka loaded with large utility pockets and a faux-fur lined hood.", "image": "assets/content/clothing/coats/Northline/coatNorthOliveParka.webp", "store": "storeClothingA", "slot": "coat", "silhouette": "parka", "baseLooks": 5, "price": 95, "quality": "Standard", "warmth": 4, "sexinessScore": 1, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "outdoor", "winter"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_north_navy_windbreaker", "name": "Navy Nylon Windbreaker", "brand": "Northline", "desc": "A lightweight navy blue nylon windbreaker. Perfect for stopping cold breezes and light rain.", "image": "assets/content/clothing/coats/Northline/coatNorthNavyWindbreaker.webp", "store": "storeClothingA", "slot": "coat", "silhouette": "windbreaker", "baseLooks": 4, "price": 45, "quality": "Basic", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty", "outdoor"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_north_red_flannel_shacket", "name": "Red Plaid Flannel Shacket", "brand": "Northline", "desc": "A 'shacket' (shirt-jacket) in a bold red and black plaid flannel. The ultimate cozy outerwear for autumn.", "image": "assets/content/clothing/coats/Northline/coatNorthRedFlannelShacket.webp", "store": "storeClothingA", "slot": "coat", "silhouette": "shacket", "baseLooks": 5, "price": 50, "quality": "Standard", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cozy", "outdoor"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_north_brown_corduroy", "name": "Brown Corduroy Jacket", "brand": "Northline", "desc": "A rich chocolate brown corduroy jacket finished with a soft sherpa fleece collar.", "image": "assets/content/clothing/coats/Northline/coatNorthBrownCorduroy.webp", "store": "storeClothingA", "slot": "coat", "silhouette": "jacket", "baseLooks": 6, "price": 70, "quality": "Standard", "warmth": 3, "sexinessScore": 2, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cozy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_north_grey_fleece", "name": "Grey Zip-Up Fleece", "brand": "Northline", "desc": "A delightfully soft heather grey fleece zip-up. Essential for quick errands on chilly mornings.", "image": "assets/content/clothing/coats/Northline/coatNorthGreyFleece.webp", "store": "storeClothingA", "slot": "coat", "silhouette": "fleece", "baseLooks": 3, "price": 40, "quality": "Basic", "warmth": 2, "sexinessScore": 0, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cozy", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_vera_camel_trench", "name": "Camel Classic Trench Coat", "brand": "VERA Mode", "desc": "A timeless camel-colored trench coat featuring sharp lapels and a waist-cinching matching belt.", "image": "assets/content/clothing/coats/VeraMode/coatVeraCamelTrench.webp", "store": "storeClothingB", "slot": "coat", "silhouette": "trench", "baseLooks": 7, "price": 120, "quality": "Premium", "warmth": 2, "sexinessScore": 3, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "office", "outdoor"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_vera_black_blazer", "name": "Tailored Black Blazer", "brand": "VERA Mode", "desc": "An impeccably tailored black blazer cut to flatter the figure while commanding absolute authority.", "image": "assets/content/clothing/coats/VeraMode/coatVeraBlackBlazer.webp", "store": "storeClothingB", "slot": "coat", "silhouette": "blazer", "baseLooks": 8, "price": 100, "quality": "Premium", "warmth": 1, "sexinessScore": 5, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "office", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_vera_navy_wool_oversized", "name": "Navy Oversized Wool Coat", "brand": "VERA Mode", "desc": "A luxuriously soft navy blue wool blend overcoat with an effortlessly chic plunging lapel draping to the knees.", "image": "assets/content/clothing/coats/VeraMode/coatVeraNavyWoolOversized.webp", "store": "storeClothingB", "slot": "coat", "silhouette": "overcoat", "baseLooks": 7, "price": 160, "quality": "Premium", "warmth": 4, "sexinessScore": 2, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "winter", "office"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_vera_white_boucle", "name": "White Bouclé Tweed Jacket", "brand": "VERA Mode", "desc": "An elite white bouclé-tweed jacket embellished with polished gold buttons and frayed trim edges.", "image": "assets/content/clothing/coats/VeraMode/coatVeraWhiteBoucle.webp", "store": "storeClothingB", "slot": "coat", "silhouette": "boucle-jacket", "baseLooks": 8, "price": 140, "quality": "Premium", "warmth": 2, "sexinessScore": 4, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_vera_houndstooth_overcoat", "name": "Classic Houndstooth Coat", "brand": "VERA Mode", "desc": "A heavy long overcoat decorated with a distinguished black and cream houndstooth pattern.", "image": "assets/content/clothing/coats/VeraMode/coatVeraHoundstoothOvercoat.webp", "store": "storeClothingB", "slot": "coat", "silhouette": "overcoat", "baseLooks": 7, "price": 130, "quality": "Premium", "warmth": 4, "sexinessScore": 3, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "office", "winter"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_fifth_black_leather_moto", "name": "Cropped Moto Leather Jacket", "brand": "Fifth Avenue", "desc": "An aggressively cropped matte black leather motorcycle jacket heavy with silver hardware zips.", "image": "assets/content/clothing/coats/FifthAvenue/coatsFifthBlackLeatherMoto.webp", "store": "storeClothingC", "slot": "coat", "silhouette": "moto-jacket", "baseLooks": 6, "price": 95, "quality": "Premium", "warmth": 2, "sexinessScore": 5, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "edgy", "party"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_fifth_vintage_bomber", "name": "Oversized Vintage Bomber", "brand": "Fifth Avenue", "desc": "A massively oversized nylon bomber jacket in faded olive green with a flash of emergency orange interior lining.", "image": "assets/content/clothing/coats/FifthAvenue/coatsFifthVintageBomber.webp", "store": "storeClothingC", "slot": "coat", "silhouette": "bomber", "baseLooks": 6, "price": 85, "quality": "Premium", "warmth": 3, "sexinessScore": 3, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "edgy", "streetwear"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_fifth_sherpa_denim", "name": "Sherpa Trucker Denim", "brand": "Fifth Avenue", "desc": "A heavy stone-washed denim trucker jacket bursting with warm, thick cream sherpa fleece from the collar.", "image": "assets/content/clothing/coats/FifthAvenue/coatFifthSherpaDenim.webp", "store": "storeClothingC", "slot": "coat", "silhouette": "trucker-jacket", "baseLooks": 5, "price": 75, "quality": "Premium", "warmth": 4, "sexinessScore": 2, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cozy", "streetwear", "winter"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_fifth_matrix_vinyl", "name": "Black Vinyl Matrix Coat", "brand": "Fifth Avenue", "desc": "A blindingly glossy black PVC vinyl long coat. It cuts a striking, squeaky, sci-fi silhouette.", "image": "assets/content/clothing/coats/FifthAvenue/coatsFifthMatrixVinyl.webp", "store": "storeClothingC", "slot": "coat", "silhouette": "trench", "baseLooks": 7, "price": 110, "quality": "Premium", "warmth": 2, "sexinessScore": 6, "exposureLevel": 1, "durability": 100, "reqCorruption": 1, "matchSet": null, "tags": ["edgy", "party"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_fifth_red_racing", "name": "Red Faux-Leather Racing Jacket", "brand": "Fifth Avenue", "desc": "A vibrant red faux-leather racing jacket styled with bold white side stripes and padded ribbed shoulders.", "image": "assets/content/clothing/coats/FifthAvenue/coatsFifthRedRacing.webp", "store": "storeClothingC", "slot": "coat", "silhouette": "moto-jacket", "baseLooks": 7, "price": 80, "quality": "Premium", "warmth": 2, "sexinessScore": 4, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "edgy", "streetwear"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_sports_black_windrunner", "name": "Black Tech Windrunner", "brand": "FastBreak", "desc": "A fully waterproof, ultra-lightweight black windrunner jacket. Perfect for outdoor cardio.", "image": "assets/content/clothing/coats/FastBreak/coatSportsBlackWindrunner.webp", "store": "storeSports", "slot": "coat", "silhouette": "windbreaker", "baseLooks": 4, "price": 60, "quality": "Standard", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["sporty", "outdoor"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_sports_grey_track_jacket", "name": "Grey Knit Track Jacket", "brand": "FastBreak", "desc": "A stretchy, breathable heather grey track jacket defining the waist for a flattering athletic look.", "image": "assets/content/clothing/coats/FastBreak/coatSportsGreyTrackJacket.webp", "store": "storeSports", "slot": "coat", "silhouette": "track-jacket", "baseLooks": 4, "price": 55, "quality": "Standard", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_sports_neon_running", "name": "Neon Reflective Runner", "brand": "FastBreak", "desc": "An intensely bright neon yellow lightweight jacket covered in high-visibility silver reflective strips.", "image": "assets/content/clothing/coats/FastBreak/coatSportsNeonRunning.webp", "store": "storeSports", "slot": "coat", "silhouette": "windbreaker", "baseLooks": 3, "price": 70, "quality": "Standard", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["sporty", "outdoor"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_sports_white_hoodie", "name": "White Terry Zip Hoodie", "brand": "FastBreak", "desc": "A thick, heavy white french-terry zip-up hoodie. The absolute pinnacle of comfortable post-workout gear.", "image": "assets/content/clothing/coats/FastBreak/coatSportsWhiteHoodie.webp", "store": "storeSports", "slot": "coat", "silhouette": "hoodie", "baseLooks": 4, "price": 50, "quality": "Basic", "warmth": 3, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty", "cozy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "coat_sports_navy_sideline", "name": "Navy Sideline Parka", "brand": "FastBreak", "desc": "An incredibly long and incredibly bloated waterproof navy sideline coat worn rigidly by athletes between plays.", "image": "assets/content/clothing/coats/FastBreak/coatSportsNavySideline.webp", "store": "storeSports", "slot": "coat", "silhouette": "parka", "baseLooks": 3, "price": 100, "quality": "Standard", "warmth": 5, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["sporty", "winter", "outdoor"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false}
]>>/* Wardrobe System Configuration
* Categories and slot mappings for the wardrobe system
*/
/* Initialize clothingData object if not exists */
<<set setup.clothingData = setup.clothingData || {}>>
/* Category definitions */
<<set setup.wardrobeCategories = [
{ group: "Outerwear", items: [
{ id: "coats", name: "Coats", slot: "coat" },
{ id: "tops", name: "Tops", slot: "top" },
{ id: "bottoms", name: "Bottoms", slot: "bottom" },
{ id: "dresses", name: "Dresses", slot: "dress" },
{ id: "shoes", name: "Shoes", slot: "shoes" },
{ id: "socks", name: "Socks", slot: "socks" }
]},
{ group: "Underwear", items: [
{ id: "bodysuits", name: "Bodysuits", slot: "bodysuit" },
{ id: "swimsuits", name: "Swimsuits", slot: "swimsuit" },
{ id: "bikiniTops", name: "Bikini Tops", slot: "bra" },
{ id: "bikiniBottoms", name: "Bikini Bottoms", slot: "panty" },
{ id: "bras", name: "Bras", slot: "bra" },
{ id: "panties", name: "Panties", slot: "panty" },
{ id: "sleepwear", name: "Sleepwear", slot: "sleepwear" },
{ id: "garter", name: "Garter", slot: "garter" }
]},
{ group: "Accessories", items: [
{ id: "earrings", name: "Earrings", slot: "earrings" },
{ id: "necklaces", name: "Necklaces", slot: "necklace" },
{ id: "bracelets", name: "Bracelets", slot: "bracelet" },
{ id: "rings", name: "Rings", slot: "ring" }
]},
{ group: "Other", items: [
{ id: "bags", name: "Bags", slot: "bag" },
{ id: "apron", name: "Apron", slot: "apron" }
]}
]>>
/* Slot labels for display */
<<set setup.slotLabels = {
top: "Top",
bottom: "Bottom",
dress: "Dress",
shoes: "Shoes",
socks: "Socks",
coat: "Coat",
bag: "Bag",
earrings: "Earrings",
necklace: "Necklace",
bracelet: "Bracelet",
ring: "Rings",
bodysuit: "Bodysuit",
bra: "Bra",
panty: "Panties",
sleepwear: "Sleepwear",
garter: "Garter",
apron: "Apron"
}>>
/* Quality colors for UI */
<<set setup.qualityColors = {
Common: "#666",
Rare: "#3b82f6",
Premium: "#a855f7"
}>>/* dresses DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.dresses = [
{"id": "dress_summer_owned", "name": "Everyday Lined Dress", "brand": "", "desc": "Simple casual dress with built-in slip lining, knee-length, long sleeves to the wrist, modest.", "image": "assets/content/clothing/dress/Starter/dress_summer_owned.webp", "store": "", "slot": "dress", "silhouette": "lined dress long sleeve", "baseLooks": 3, "price": 0, "quality": "Common", "warmth": 2, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": false, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_basic_cotton_sundress_t0", "name": "Cotton Sundress", "brand": "Northline", "desc": "A simple, easy-going warm yellow cotton sundress with a relaxed fit and spaghetti straps.", "image": "assets/content/clothing/dress/Northline/dressClothingABasicCottonSundressT0.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 2, "price": 35, "quality": "Common", "warmth": 0, "sexinessScore": 3, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_floral_mini_dress_t1", "name": "Floral Mini Dress", "brand": "Northline", "desc": "A cute, flirty mini dress covered in a vibrant ditsy floral print with short puff sleeves.", "image": "assets/content/clothing/dress/Northline/dressClothingAFloralMiniDressT1.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_ribbed_knit_midi_t1", "name": "Ribbed Knit Midi", "brand": "Northline", "desc": "A comfortable and trendy olive-green short-sleeved midi dress forged from a stretchy ribbed knit fabric.", "image": "assets/content/clothing/dress/Northline/dressClothingARibbedKnitMidiT1.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 50, "quality": "Common", "warmth": 2, "sexinessScore": 4, "exposureLevel": 3, "durability": 60, "reqCorruption": 1, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_denim_pinafore_t2", "name": "Denim Pinafore", "brand": "Northline", "desc": "A playful light blue denim pinafore dress with metal button straps, perfect for layering over tees.", "image": "assets/content/clothing/dress/Northline/dressClothingADenimPinaforeT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 60, "quality": "Rare", "warmth": 1, "sexinessScore": 6, "exposureLevel": 7, "durability": 60, "reqCorruption": 5, "tags": ["casual", "cute", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 50, "reqExhibitionism": 20, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_wrap_polka_dot_t2", "name": "Polka Dot Wrap", "brand": "Northline", "desc": "A breezy navy blue wrap dress speckled with tiny white polka dots, tying cleanly at the waist with a romantic V-neck.", "image": "assets/content/clothing/dress/Northline/dressClothingAWrapPolkaDotT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_basic_bodycon_mini_t3", "name": "Basic Bodycon", "brand": "Northline", "desc": "A tightly fitted, stretchy black jersey tube-style mini dress that flawlessly grabs every curve.", "image": "assets/content/clothing/dress/Northline/dressClothingABasicBodyconMiniT3.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 4, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 60, "reqCorruption": 5, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_slogan_tshirt_dress_t1", "name": "Slogan T-Shirt Dress", "brand": "Northline", "desc": "An oversized white t-shirt dress featuring a bold, colorful 90s graphic logo printed prominently on the chest.", "image": "assets/content/clothing/dress/Northline/dressClothingASloganTShirtDressT1.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 35, "quality": "Common", "warmth": 1, "sexinessScore": 4, "exposureLevel": 5, "durability": 60, "reqCorruption": 3, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_star_slip_dress_t3", "name": "Star Velvet Slip", "brand": "Northline", "desc": "A slightly edgy, dark navy blue velvet slip dress elegantly stamped with silver foil star patterns and extremely thin string straps.", "image": "assets/content/clothing/dress/Northline/dressClothingAStarSlipDressT3.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 4, "price": 55, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 60, "reqCorruption": 3, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_stripe_shirt_dress_t1", "name": "Stripe Shirt Dress", "brand": "Northline", "desc": "A crisp, oversized blue and white vertically striped woven cotton button-down shirt styled comfortably as a mini dress.", "image": "assets/content/clothing/dress/Northline/dressClothingAStripeShirtDressT1.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 45, "quality": "Common", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 60, "reqCorruption": 4, "tags": ["casual", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_acid_denim_zip_t2", "name": "Acid Wash Zip Dress", "brand": "Northline", "desc": "A heavily textured 90s-style acid wash grey denim dress featuring an exposed heavy metal zipper straight down the middle.", "image": "assets/content/clothing/dress/Northline/dressClothingAAcidDenimZipT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 4, "price": 65, "quality": "Rare", "warmth": 1, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "tags": ["casual", "sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_gingham_smock_t1", "name": "Gingham Smock", "brand": "Northline", "desc": "An adorable, extremely voluminous light pink and white gingham check smock dress with a softly gathered tiered skirt.", "image": "assets/content/clothing/dress/Northline/dressClothingAGinghamSmockT1.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 2, "price": 50, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_tropical_halter_maxi_t2", "name": "Tropical Halter Maxi", "brand": "Northline", "desc": "A dramatically long flowing beach maxi dress splashed vigorously with huge colorful oversized bright green tropical palm leaves.", "image": "assets/content/clothing/dress/Northline/dressClothingATropicalHalterMaxiT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 4, "price": 70, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 60, "reqCorruption": 1, "tags": ["casual", "elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_tie_dye_ribbed_tank_t1", "name": "Tie-Dye Tank Dress", "brand": "Northline", "desc": "A tight, stretchy casual ribbed tank dress splashed randomly entirely with a colorful swirling psychedelic pastel tie-dye pattern.", "image": "assets/content/clothing/dress/Northline/dressClothingATieDyeRibbedTankT1.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 2, "price": 35, "quality": "Common", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 60, "reqCorruption": 3, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_plaid_skater_t2", "name": "Plaid Skater Dress", "brand": "Northline", "desc": "A punk-inspired red and black tartan plaid sleeveless skater dress featuring a dramatically flared circle skirt.", "image": "assets/content/clothing/dress/Northline/dressClothingAPlaidSkaterT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 55, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 4, "durability": 60, "reqCorruption": 3, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_faux_leather_pinafore_t3", "name": "Leather Pinafore", "brand": "Northline", "desc": "A tough yet alluring black faux leather mini pinafore dress secured efficiently with bold thick metal shoulder buckles.", "image": "assets/content/clothing/dress/Northline/dressClothingAFauxLeatherPinaforeT3.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 4, "price": 65, "quality": "Premium", "warmth": 2, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "tags": ["sexy", "casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_color_block_polo_t1", "name": "Color-Block Polo", "brand": "Northline", "desc": "A preppy, tennis-inspired color-block polo dress cleanly dividing the body in horizontal bold sporty stripes of blue, red, and white.", "image": "assets/content/clothing/dress/Northline/dressClothingAColorBlockPoloT1.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 45, "quality": "Common", "warmth": 1, "sexinessScore": 2, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "tags": ["casual", "sporty", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_floral_peasant_t2", "name": "Floral Peasant Dress", "brand": "Northline", "desc": "A bohemian-style lightweight cotton peasant dress with a square neckline, long billowy sleeves, and dark rustic floral prints.", "image": "assets/content/clothing/dress/Northline/dressClothingAFloralPeasantT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 55, "quality": "Rare", "warmth": 3, "sexinessScore": 1, "exposureLevel": 1, "durability": 60, "reqCorruption": 1, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_corduroy_dungaree_t2", "name": "Corduroy Dungaree", "brand": "Northline", "desc": "A vintage mustard-yellow thick corduroy dungaree overall dress featuring thick buckle straps and a large front pocket.", "image": "assets/content/clothing/dress/Northline/dressClothingACorduroyDungareeT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 60, "quality": "Rare", "warmth": 2, "sexinessScore": 7, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "tags": ["casual", "cute", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 60, "reqExhibitionism": 20, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_ribbed_turtleneck_mini_t1", "name": "Ribbed Turtleneck", "brand": "Northline", "desc": "A snug, long-sleeved dark grey ribbed knit mini dress extending up to form a cozy folded turtleneck collar.", "image": "assets/content/clothing/dress/Northline/dressClothingARibbedTurtleneckMiniT1.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 45, "quality": "Common", "warmth": 4, "sexinessScore": 5, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_poplin_puff_sleeve_t2", "name": "Poplin Puff-Sleeve", "brand": "Northline", "desc": "A crisp white poplin cotton mini dress distinguished by dramatic oversized puffy shoulders and a square neckline.", "image": "assets/content/clothing/dress/Northline/dressClothingAPoplinPuffSleeveT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 4, "price": 55, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 4, "durability": 60, "reqCorruption": 3, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_striped_linen_blend_t2", "name": "Striped Linen Dress", "brand": "Northline", "desc": "An effortlessly chic, breathable beige linen blend midi dress decorated clearly by subtle vertical white pinstripes and front buttons.", "image": "assets/content/clothing/dress/Northline/dressClothingAStripedLinenBlendT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 60, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 60, "reqCorruption": 1, "tags": ["casual", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_smocked_midi_t2", "name": "Smocked Bodice Midi", "brand": "Northline", "desc": "A soft olive-green midi dress featuring a highly elasticized smocked bodice dropping into a flowing skirt with thin shoulder straps.", "image": "assets/content/clothing/dress/Northline/dressClothingASmockedMidiT2.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 3, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 60, "reqCorruption": 1, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_crochet_beach_t3", "name": "Crochet Beach Dress", "brand": "Northline", "desc": "A fully sheer, loose-knit beige crochet maxi dress designed to gracefully reveal the skin or bikini underneath for a relaxing beach vibe.", "image": "assets/content/clothing/dress/Northline/dressClothingACrochetBeachT3.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 4, "price": 80, "quality": "Premium", "warmth": 0, "sexinessScore": 9, "exposureLevel": 9, "durability": 60, "reqCorruption": 6, "tags": ["sexy", "slutty", "lewd"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 80, "reqExhibitionism": 80, "reqHeelsSkill": 0},
{"id": "dress_clothing_a_metallic_slip_t3", "name": "Metallic Party Slip", "brand": "Northline", "desc": "A dazzling, fluidly draping thin gold metallic woven slip dress cut precisely featuring a cowl neck and highly attractive thin string straps.", "image": "assets/content/clothing/dress/Northline/dressClothingAMetallicSlipT3.webp", "store": "storeClothingA", "slot": "dress", "baseLooks": 4, "price": 90, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 60, "reqCorruption": 5, "tags": ["sexy", "elegant", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_satin_slip_midi_t2", "name": "Blush Satin Slip Midi", "brand": "VERA Mode", "desc": "A minimalist blush-pink satin-finish slip midi dress with thin adjustable straps and a gently bias-cut skirt.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBSatinSlipMidiT2.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 4, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "tags": ["cute", "sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_asym_hem_cutout_t3", "name": "Asymmetric Cutout Dress", "brand": "VERA Mode", "desc": "A sleek black jersey dress featuring a bold side cutout at the waist and a dramatic asymmetric hemline short on one side, knee-length on the other.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBAsymHemCutoutT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 4, "price": 100, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_leopard_wrap_midi_t3", "name": "Leopard Wrap Midi", "brand": "VERA Mode", "desc": "A bold leopard print wrap midi dress in rust and caramel tones with a plunging V-neckline, long sleeves, and a side-tied draped skirt.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBLeopardWrapMidiT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 90, "quality": "Rare", "warmth": 2, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "tags": ["sexy", "cute", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_ruched_bodycon_t3", "name": "Ruched Bodycon Mini", "brand": "VERA Mode", "desc": "A figure-hugging ruched stretch satin mini dress in deep forest green, with a square neckline and gathered fabric detailing all across the body.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBRuchedBodyconT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 85, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_corduroy_shirt_dress_t2", "name": "Caramel Corduroy Shirt Dress", "brand": "VERA Mode", "desc": "A relaxed A-line corduroy shirt dress in warm caramel brown, featuring a full button front placket, a belted waist, and a knee-grazing length.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBCorduroyShirtDressT2.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 4, "price": 70, "quality": "Rare", "warmth": 3, "sexinessScore": 2, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_abstract_print_midi_t3", "name": "Abstract Print Halter Midi", "brand": "VERA Mode", "desc": "A flowy halter-neck midi dress printed with a bold abstract brushstroke pattern in deep blue and terracotta on a cream base.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBAbstractPrintMidiT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 4, "price": 95, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "tags": ["elegant", "sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_mono_knit_long_sleeve_t2", "name": "Tonal Knit Long-Sleeve Dress", "brand": "VERA Mode", "desc": "A chic monochromatic ivory ribbed-knit long-sleeve dress with a subtle turtleneck and a clean column silhouette falling to just below the knee.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBMonoKnitLongSleeveT2.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 4, "price": 80, "quality": "Rare", "warmth": 3, "sexinessScore": 3, "exposureLevel": 2, "durability": 60, "reqCorruption": 1, "tags": ["casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_faux_leather_mini_t4", "name": "Faux Leather Mini Dress", "brand": "VERA Mode", "desc": "A provocative matte black faux leather sleeveless mini dress with a zip-front closure and a structured, fitted silhouette ending high on the thigh.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBFauxLeatherMiniT4.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 115, "quality": "Premium", "warmth": 1, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 5, "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_paillette_open_back_t4", "name": "Sequin Open-Back Mini", "brand": "VERA Mode", "desc": "A dazzling all-over silver sequin mini dress featuring a barely-there open back with a single cross-strap and a fitted tube silhouette.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBPailletteOpenBackT4.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 135, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "tags": ["sexy", "revealing", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_stripe_knit_mini_t3", "name": "Bold Stripe Knit Mini", "brand": "VERA Mode", "desc": "A trendy wide horizontal stripe knit mini dress in contrasting black and ecru, with a boat neckline and short balloon sleeves.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBStripeKnitMiniT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 3, "price": 75, "quality": "Rare", "warmth": 2, "sexinessScore": 4, "exposureLevel": 4, "durability": 60, "reqCorruption": 3, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_tiered_flounce_t2", "name": "Cascading Flounce Midi", "brand": "VERA Mode", "desc": "A romantic dusty rose tiered flounce midi dress with thin straps, each tier of sheer chiffon cascading in ruffled layers to the ankle.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBTieredFlounceT2.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 4, "price": 80, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "tags": ["cute", "casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 30, "reqExhibitionism": 20, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_velvet_bodice_slip_t3", "name": "Velvet Bodice Slip Dress", "brand": "VERA Mode", "desc": "A sultry deep burgundy slip dress with a plush velvet fitted bodice and a silky satin bias-cut skirt flowing to the knee, combining two textures dramatically.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBVelvetBodiceSlipT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 105, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "tags": ["sexy", "elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_crochet_cover_up_t3", "name": "Crochet Knit Mini Dress", "brand": "VERA Mode", "desc": "A boho chic cream crochet open-knit mini dress with a square neckline, short fringe hem trim, and a handcrafted lace-like pattern visible through the open weave.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBCrochetCoverUpT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 4, "price": 100, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 60, "reqCorruption": 5, "tags": ["sexy", "revealing", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 60, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_off_shoulder_floral_t3", "name": "Off-Shoulder Floral Party Dress", "brand": "VERA Mode", "desc": "A fitted mini dress with a dramatic off-shoulder straight neckline adorned with a wide ruffled frill, printed in a bold red tropical floral on white.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBOffShoulderFloralT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 90, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "tags": ["cute", "sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_high_neck_bodycon_t4", "name": "High Neck Cutout Bodycon", "brand": "VERA Mode", "desc": "An ultra-tight black stretch mini dress with a sleeveless high mock neck and two bold oval cutouts exposing the midriff and lower abdomen just above the short hem.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBHighNeckBodyconT4.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 110, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_tulle_skirt_combo_t3", "name": "Satin Bodice Tulle Midi", "brand": "VERA Mode", "desc": "A dreamy cocktail-style dress combining a fitted dusty blue satin strapless bodice with a voluminous knee-length tulle skirt layered for dramatic fullness.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBTulleSkirtComboT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 100, "quality": "Premium", "warmth": 0, "sexinessScore": 5, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "tags": ["cute", "sexy", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_shimmer_knit_tube_t4", "name": "Shimmer Knit Tube Dress", "brand": "VERA Mode", "desc": "A bodycon strapless tube mini dress in a metallic gold shimmer knit, hugging every curve with extreme tightness and ending daringly high on the thigh.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBShimmerKnitTubeT4.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 5, "price": 125, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_b_lace_insert_midi_t3", "name": "Lace Panel Satin Midi", "brand": "VERA Mode", "desc": "An elegant champagne satin midi dress featuring delicate floral lace panel inserts along the sides and a deep V-back, with a classic sleeveless silhouette.", "image": "assets/content/clothing/dress/VERAMode/dressClothingBLaceInsertMidiT3.webp", "store": "storeClothingB", "slot": "dress", "baseLooks": 4, "price": 120, "quality": "Premium", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 45, "reqExhibitionism": 10, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_crepe_midi_t3", "name": "Midnight Crepe Wrap Midi", "brand": "Fifth Avenue Wear", "desc": "A sophisticated deep navy crepe wrap midi dress with a V-neckline, long sleeves, and a clean tailored silhouette that falls just below the knee a timeless power-dressing staple.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCCrepeMidiT3.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 5, "price": 160, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 2, "durability": 60, "reqCorruption": 2, "tags": ["elegant", "professional"], "npcAppeal": {"professional": 2, "elite": 1}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_jacquard_sheath_t3", "name": "Ivory Jacquard Sheath Dress", "brand": "Fifth Avenue Wear", "desc": "A structured ivory sheath dress woven from a subtle floral jacquard fabric, featuring a round neckline, short sleeves, and a perfectly fitted knee-length silhouette.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCJacquardSheathT3.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 5, "price": 180, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 2, "durability": 60, "reqCorruption": 2, "tags": ["elegant", "formal", "professional"], "npcAppeal": {"professional": 2, "elite": 2}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_lace_bodycon_t3", "name": "Lace-Panel Bodycon Dress", "brand": "Fifth Avenue Wear", "desc": "A rich burgundy scuba-crepe bodycon dress with elegant long-sleeve lace overlay panels along the arms and sides, creating a refined contrast of texture at a cocktail-ready knee length.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCLaceBodyconT3.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 6, "price": 190, "quality": "Premium", "warmth": 2, "sexinessScore": 7, "exposureLevel": 7, "durability": 60, "reqCorruption": 5, "tags": ["elegant", "sexy", "revealing"], "npcAppeal": {"professional": 1, "elite": 2}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 55, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_tailored_pencil_t3", "name": "Charcoal Tailored Pencil Dress", "brand": "Fifth Avenue Wear", "desc": "A sharp, impeccably tailored sleeveless pencil dress in deep charcoal with subtle fine-stripe suiting fabric, structured princess seams, and a pencil-slim silhouette to the knee.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCTailoredPencilT3.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 5, "price": 165, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 2, "durability": 60, "reqCorruption": 2, "tags": ["elegant", "professional", "formal"], "npcAppeal": {"professional": 3, "elite": 1}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_satin_evening_midi_t4", "name": "Deep Emerald Satin Evening Dress", "brand": "Fifth Avenue Wear", "desc": "An opulent deep emerald satin evening midi dress with a cowl neckline, draped open back, and a fluid bias-cut skirt flowing to mid-calf effortless old-Hollywood glamour.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCSatinEveningMidiT4.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 5, "price": 220, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "tags": ["elegant", "formal", "sexy"], "npcAppeal": {"elite": 3}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_fine_stripe_shirt_t3", "name": "Fine Stripe Shirt Dress", "brand": "Fifth Avenue Wear", "desc": "A sophisticated monochrome fine-stripe poplin shirt dress with a structured collar, concealed button placket, belted waist, and a tailored knee-length A-line skirt.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCFineStripeShirtT3.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 5, "price": 150, "quality": "Rare", "warmth": 2, "sexinessScore": 1, "exposureLevel": 1, "durability": 60, "reqCorruption": 2, "tags": ["elegant", "professional"], "npcAppeal": {"professional": 3}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_velvet_midi_t4", "name": "Royal Blue Velvet Midi", "brand": "Fifth Avenue Wear", "desc": "A luxurious deep royal blue crushed velvet midi dress with a fitted long-sleeve bodice, a subtle cowl neckline, and a softly flared below-knee skirt an evening statement piece.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCVelvetMidiT4.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 5, "price": 215, "quality": "Premium", "warmth": 3, "sexinessScore": 2, "exposureLevel": 1, "durability": 60, "reqCorruption": 1, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 3}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_one_shoulder_long_t4", "name": "One-Shoulder Crepe Column Gown", "brand": "Fifth Avenue Wear", "desc": "A sleek floor-length one-shoulder matte black crepe column gown with a draped shoulder detail and a long thigh-high leg slit formal minimalist perfection.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCOneShoulderLongT4.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 5, "price": 225, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 60, "reqCorruption": 4, "tags": ["elegant", "formal", "sexy"], "npcAppeal": {"elite": 3}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "dress_clothing_c_tweed_boucle_mini_t3", "name": "Ivory Tweed Bouclé Mini Dress", "brand": "Fifth Avenue Wear", "desc": "A classic Chanel-inspired ivory and gold-fleck bouclé tweed structured mini dress with a jewel neckline, 3/4 sleeves, and contrast braid trim at the hem and cuffs.", "image": "assets/content/clothing/dress/FifthAvenueWear/dressClothingCTweedBoucleMiniT3.webp", "store": "storeClothingC", "slot": "dress", "baseLooks": 5, "price": 205, "quality": "Premium", "warmth": 2, "sexinessScore": 3, "exposureLevel": 3, "durability": 60, "reqCorruption": 3, "tags": ["elegant", "formal", "professional"], "npcAppeal": {"professional": 2, "elite": 3}, "shopAvailable": true, "startOwned": false, "matchSet": null, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* earrings DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.earrings = [
{"id": "jewel_er_studs_crystal_simple_t2", "name": "Classic Crystal Studs", "brand": "Diamond Dreams", "desc": "Timeless, simple, and elegant. A perfectly matched pair of brilliant round-cut cubic zirconia studs set in sterling silver baskets.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERStudsCrystalSimpleT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 6, "price": 355, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_er_hoops_gold_chunky_t3", "name": "Chunky Gold Hoops", "brand": "Diamond Dreams", "desc": "Bold and unapologetic. Thick, heavy, massive hollow 18k gold-plated circular hoop earrings that dominate the profile.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERHoopsGoldChunkyT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 7, "price": 405, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_er_drop_pearl_gold_t4", "name": "Baroque Pearl Drops", "brand": "Diamond Dreams", "desc": "Ornate dangling elegance. A heavy, uneven natural baroque pearl drops gracefully from intricate yellow gold filigree posts.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERDropPearlGoldT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 8, "price": 370, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_er_threader_silver_star_t2", "name": "Silver Star Threaders", "brand": "Diamond Dreams", "desc": "A very modern minimalist threader earring. An incredibly long, fragile silver chain pulls completely through the ear, terminating in tiny stars.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERThreaderSilverStarT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 6, "price": 250, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_er_cuff_ear_climber_gold_t3", "name": "Gold Vine Ear Climber", "brand": "Diamond Dreams", "desc": "An edgy asymmetrical piece. A curved golden vine adorned with tiny micro-leaves climbs sharply up the curve of the ear cartilage.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERCuffEarClimberGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 7, "price": 355, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_er_huggie_hoop_pave_silver_t2", "name": "Silver Pave Huggies", "brand": "Diamond Dreams", "desc": "Understated luxury. Tiny, severely close-fitting silver ring hoops encrusted with a single line of micro pavé crystals snapping tightly to the lobe.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERHuggieHoopPaveSilverT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 6, "price": 365, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_er_tassel_fringe_rose_gold_t3", "name": "Rose Gold Fringe Drops", "brand": "Diamond Dreams", "desc": "Playful and highly kinetic. A cascading waterfall of tiny, fluid rose gold chains bundled together into soft swishing metallic tassels.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERTasselFringeRoseGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 7, "price": 315, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_er_sapphire_halo_studs_silver_t4", "name": "Sapphire Halo Studs", "brand": "Diamond Dreams", "desc": "Breathtakingly elegant deep blue sapphire stones, perfectly oval-cut and framed generously tightly by a blinding halo ring of micro-crystals.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERSapphireHaloStudsSilverT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 9, "price": 450, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 75, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_er_irregular_resin_tortoise_t2", "name": "Tortoise Resin Teardrops", "brand": "Diamond Dreams", "desc": "A heavy, chunky casual pair of earrings shaped loosely into irregular organic teardrops, poured from deep brown and amber tortoiseshell acrylic resin.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelERIrregularResinTortoiseT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "earrings", "baseLooks": 6, "price": 355, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* Garter DB reset */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.garter = [
{"id": "garter_sl_basic_black_sheer", "name": "Black Sheer Garter Belt", "brand": "Silk & Lace", "desc": "A simple everyday black sheer mesh garter belt with four adjustable elastic suspender straps and subtle lace edging.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLBasicBlackSheer.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "4-strap", "baseLooks": 4, "price": 15, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 15, "reqHeelsSkill": 0},
{"id": "garter_sl_satin_red_bow", "name": "Red Satin Bow Garter Belt", "brand": "Silk & Lace", "desc": "Classic 4-strap red satin garter belt decorated with small contrasting black bows at the top of each strap.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLSatinRedBow.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "4-strap", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_sl_lace_white_wide", "name": "Wide White Lace Garter", "brand": "Silk & Lace", "desc": "A bridal-inspired wide white floral lace garter belt providing mild shaping, with six thin suspender straps for a secure hold.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLLaceWhiteWide.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "6-strap", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 80, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_sl_ribbon_satin_blush_set", "name": "Dusty Blush Ribbon Garter", "brand": "Silk & Lace", "desc": "Delicate dusty blush satin garter belt with lace trim and ruched ribbon strap details. Part of the silkBlushRibbonSet.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLRibbonSatinBlushSet.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "4-strap", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 80, "reqCorruption": 3, "matchSet": "silkBlushRibbonSet", "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_sl_vintage_rose_print_wide", "name": "Vintage Rose Print Wide Garter", "brand": "Silk & Lace", "desc": "Wide vintage-inspired satin garter belt adorned with a romantic English rose print and thick 6-strap pink fasteners.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLVintageRosePrintWide.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "6-strap", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 80, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_sl_strappy_emerald_lace", "name": "Emerald Strappy Lace Garter", "brand": "Silk & Lace", "desc": "Complex emerald green scalloped lace garter belt crisscrossed with multiple thin black elastic strapping details at the hips.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLStrappyEmeraldLace.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "4-strap", "baseLooks": 6, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 80, "reqCorruption": 5, "matchSet": "silkEmeraldStrappySet", "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 20, "reqHeelsSkill": 0},
{"id": "garter_sl_corset_lace_up_black", "name": "Corset Lace-Up Garter Belt", "brand": "Silk & Lace", "desc": "Deep faux-leather and mesh 6-strap garter belt with a functional corset-style ribbon lace-up closure at the front panel.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLCorsetLaceUpBlack.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "6-strap", "baseLooks": 6, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 3, "durability": 80, "reqCorruption": 5, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_sl_suspender_brief_navy", "name": "Navy Suspender Brief", "brand": "Silk & Lace", "desc": "An elegant all-in-one piece; high-waisted navy satin brief with built-in adjustable garter straps dangling from the lower edge.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLSuspenderBriefNavy.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "brief", "baseLooks": 6, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_sl_premium_rose_gold_chain", "name": "Rose Gold Chain Garter", "brand": "Silk & Lace", "desc": "A premium lingerie accessory crafted from luxurious rose gold satin straps interspersed with fine drooping rose gold chains.", "image": "assets/content/clothing/garter/SilkAndLace/garterSLPremiumRoseGoldChain.webp", "store": "storeLingerieA", "slot": "garter", "silhouette": "chain", "baseLooks": 7, "price": 50, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 4, "durability": 80, "reqCorruption": 6, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_is_classic_black_lace", "name": "Classic Black Lace Garter", "brand": "Intimate Secrets", "desc": "A wide band of elegant black floral lace tightly gripping the waist, featuring four adjustable satin straps.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISClassicBlackLace.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 6, "price": 45, "quality": "Standard", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_is_red_strappy_harness", "name": "Red Strappy Harness Garter", "brand": "Intimate Secrets", "desc": "An aggressively erotic bright red garter belt constructed entirely from thin elastic caged harness straps.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISRedStrappyHarness.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 7, "price": 55, "quality": "Premium", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["slutty", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 20, "reqHeelsSkill": 0},
{"id": "garter_is_silk_white_pearl", "name": "White Pearl Silk Garter", "brand": "Intimate Secrets", "desc": "A bridal-inspired innocent white pure silk garter delicately adorned with tiny pearls.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISSilkWhitePearl.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 6, "price": 60, "quality": "Premium", "warmth": 0, "sexinessScore": 4, "exposureLevel": 2, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["cute", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_is_leather_buckle", "name": "Leather Buckled Garter", "brand": "Intimate Secrets", "desc": "A rigid, unforgiving thick black leather garter belt locked heavily with silver metal buckles.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISLeatherBuckle.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 7, "price": 65, "quality": "Premium", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["slutty", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_is_pink_mesh_ruffle", "name": "Pink Mesh Ruffle Garter", "brand": "Intimate Secrets", "desc": "A playful, impossibly cute baby pink mesh garter bordered completely in bouncy ruffled trim.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISPinkMeshRuffle.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 5, "price": 40, "quality": "Standard", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["cute", "sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 15, "reqHeelsSkill": 0},
{"id": "garter_is_navy_velvet", "name": "Navy Velvet Garter", "brand": "Intimate Secrets", "desc": "A luxurious deep navy blue velvet garter belt supplying an incredibly rich tactile sensation.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISNavyVelvet.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 6, "price": 50, "quality": "Premium", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_is_burgundy_satin", "name": "Burgundy Satin Garter", "brand": "Intimate Secrets", "desc": "A highly reflective, wet-look burgundy satin garter belt that shimmers beautifully at the hips.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISBurgundySatin.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 5, "price": 45, "quality": "Standard", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_is_gold_chain_drop", "name": "Gold Chain Drop Garter", "brand": "Intimate Secrets", "desc": "An aggressively opulent garter belt substituting fabric entirely for heavy, swinging gold metal chains.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISGoldChainDrop.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 8, "price": 95, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 4, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["slutty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 30, "reqHeelsSkill": 0},
{"id": "garter_is_leopard_print_mesh", "name": "Leopard Print Garter", "brand": "Intimate Secrets", "desc": "A predatory and wildly sheer semi-transparent mesh garter belt violently printed with bold leopard spots.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISLeopardPrintMesh.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 5, "price": 40, "quality": "Standard", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["slutty", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 15, "reqHeelsSkill": 0},
{"id": "garter_is_emerald_lace", "name": "Emerald Jewel Lace Garter", "brand": "Intimate Secrets", "desc": "A breathtaking deep emerald green scalloped eyelash lace garter belt radiating dark, mature elegance.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISEmeraldLace.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 7, "price": 55, "quality": "Premium", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_is_hot_pink_vinyl", "name": "Hot Pink Vinyl Garter", "brand": "Intimate Secrets", "desc": "A brutally tight, artificially glossy hot neon pink PVC vinyl garter built explicitly for club nights.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISHotPinkVinyl.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 6, "price": 50, "quality": "Standard", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["slutty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "garter_is_white_fishnet_elastic", "name": "White Fishnet Garter", "brand": "Intimate Secrets", "desc": "A sporty incredibly sheer white fishnet mesh garter accented with thick white elastic bands.", "image": "assets/content/clothing/garter/IntimateSecrets/garterISWhiteFishnetElastic.webp", "store": "storeLingerieB", "slot": "garter", "silhouette": "garter-belt", "baseLooks": 4, "price": 30, "quality": "Basic", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 20, "reqHeelsSkill": 0}
]>>/* necklaces DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.necklaces = [
{"id": "jewel_nc_mini_initial_silver_t2", "name": "Silver Initial Pendant", "brand": "Diamond Dreams", "desc": "A delicate, minimalist sterling silver necklace featuring a tiny polished initial letter pendant. Perfect for everyday casual layering.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCMiniInitialSilverT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 6, "price": 345, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 75, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_pearl_drop_gold_t3", "name": "Gold Pearl Drop Necklace", "brand": "Diamond Dreams", "desc": "Classic elegance. A beautifully simple gold-plated chain suspending a single, perfectly round teardrop freshwater pearl.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCPearlDropGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 7, "price": 355, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"professional": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_chunky_layered_rose_gold_t3", "name": "Layered Rose Gold Chains", "brand": "Diamond Dreams", "desc": "A trendy pre-layered set of three thick rose gold chains of varying lengths and link sizes. Creates an immediate fashion-forward statement.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCChunkyLayeredRoseGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 7, "price": 395, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_crystal_tennis_choker_t4", "name": "Crystal Tennis Choker", "brand": "Diamond Dreams", "desc": "An intensely sparkling tennis choker. A continuous, perfectly symmetrical line of high-grade clear cubic zirconia crystals set seamlessly in white gold.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCCrystalTennisChokerT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 9, "price": 400, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_moon_star_gold_t2", "name": "Celestial Moon Pendant", "brand": "Diamond Dreams", "desc": "A cute, dainty gold chain connecting a tiny crescent moon and a matching little star. Popular casual celestial aesthetic.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCMoonStarGoldT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 6, "price": 320, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_emerald_cut_zirconia_silver_t4", "name": "Emerald-Cut Ice Pendant", "brand": "Diamond Dreams", "desc": "A breathtaking statement piece. A massive, flawless emerald-cut cubic zirconia stone resting heavily against the chest on a solid sterling cable chain.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCEmeraldCutZirconiaSilverT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 9, "price": 410, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_snake_chain_gold_t3", "name": "Thick Gold Snake Chain", "brand": "Diamond Dreams", "desc": "A heavy, sensuously smooth flat gold snake chain. Sits flat and beautifully structured across the collarbones, reflecting light like molten metal.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCSnakeChainGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 7, "price": 420, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_halo_heart_silver_t3", "name": "Silver Halo Heart", "brand": "Diamond Dreams", "desc": "A romantic silver necklace suspending a polished heart motif completely surrounded by a sparkling halo of tiny micro-crystals.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCHaloHeartSilverT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 7, "price": 365, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_pendant_sapphire_gold_t4", "name": "Sapphire Gold Teardrop", "brand": "Diamond Dreams", "desc": "A striking deep blue lab-grown sapphire teardrop stone intricately housed in an ornate vintage-style bright gold basket setting.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCPendantSapphireGoldT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 8, "price": 405, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_locket_rose_gold_t2", "name": "Rose Gold Oval Locket", "brand": "Diamond Dreams", "desc": "A sweet, nostalgic classic oval locket engraved with delicate flowers, rendered entirely in soft rosy gold.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCLocketRoseGoldT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 6, "price": 335, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_toggle_bar_silver_t3", "name": "Silver Bar Toggle Choker", "brand": "Diamond Dreams", "desc": "A very modern, heavy industrial silver chain choker. Fastens boldly directly in the front with a chunky, prominent T-bar and O-ring switch.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCToggleBarSilverT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 7, "price": 405, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_nc_drop_y_necklace_crystal_t4", "name": "Crystal Y-Drop Necklace", "brand": "Diamond Dreams", "desc": "Designed to draw the eye downwards. A dangerously elegant long Y-shaped chain that plunges deeply, terminating in a single brilliant suspended crystal.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelNCDropYNecklaceCrystalT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "necklace", "baseLooks": 8, "price": 370, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "sexy", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* Panties DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.panties = [
{"id": "panties_white_owned", "name": "White Everyday Panties", "brand": "", "desc": "Simple white everyday panties.", "image": "assets/content/clothing/panties/Starter/panties_white_owned.webp", "store": "", "slot": "panty", "silhouette": "basic panty", "baseLooks": 1, "price": 0, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": false, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panties_pink_owned", "name": "Pink Everyday Panties", "brand": "", "desc": "Simple pink everyday panties.", "image": "assets/content/clothing/panties/Starter/panties_pink_owned.webp", "store": "", "slot": "panty", "silhouette": "basic panty", "baseLooks": 1, "price": 0, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": false, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_basic_brief_white", "name": "White Cotton Everyday Brief", "brand": "Northline Apparel", "desc": "Smooth white cotton basic everyday brief with an elastic waistband and comfortable seamless lines.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthBasicBriefWhite.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "brief", "baseLooks": 1, "price": 5, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_basic_brief_black", "name": "Black Cotton Everyday Brief", "brand": "Northline Apparel", "desc": "Classic black cotton-stretch brief offering full coverage and a smooth silhouette under clothes.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthBasicBriefBlack.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "brief", "baseLooks": 1, "price": 5, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_heather_hipster_grey", "name": "Heather Grey Cotton Hipster", "brand": "Northline Apparel", "desc": "Heather grey cotton-blend hipster panty with a low-rise fit, wide logo waistband and comfortable coverage.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthHeatherHipsterGrey.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "hipster", "baseLooks": 2, "price": 5, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 1, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_seamless_bikini_nude", "name": "Nude Seamless Bikini Panty", "brand": "Northline Apparel", "desc": "Invisible nude seamless bikini-cut panty crafted from smooth laser-cut microfiber.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthSeamlessBikiniNude.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "bikini-cut", "baseLooks": 2, "price": 10, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_ribbed_cheeky_navy", "name": "Navy Ribbed Cheeky Panty", "brand": "Northline Apparel", "desc": "Navy blue ribbed stretch-cotton cheeky panty that offers medium back coverage and a flattering V-cut front.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthRibbedCheekyNavy.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "cheeky", "baseLooks": 3, "price": 10, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_seamless_high_waist_white", "name": "White Seamless High-Waist Panty", "brand": "Northline Apparel", "desc": "Crisp white high-waist seamless compression panty offering soft tummy support and full coverage.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthSeamlessHighWaistWhite.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "high-waist", "baseLooks": 2, "price": 10, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_cotton_thong_olive", "name": "Olive Cotton Basic Thong", "brand": "Northline Apparel", "desc": "Dusty olive green cotton thong with a thin elastic waistband and minimal back coverage for a completely seamless look.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthCottonThongOlive.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "thong", "baseLooks": 3, "price": 10, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_micro_mesh_cheeky_black", "name": "Black Micro-Mesh Cheeky Panty", "brand": "Northline Apparel", "desc": "Black semi-sheer micro-mesh cheeky panty providing breathable comfort with a sporty, slightly revealing edge.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthMicroMeshCheekyBlack.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "cheeky", "baseLooks": 4, "price": 10, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 35, "reqHeelsSkill": 0},
{"id": "panty_north_ribbed_high_waist_rose", "name": "Dusty Rose Ribbed High-Waist Brief", "brand": "Northline Apparel", "desc": "Dusty rose thick ribbed cotton high-waist brief with a highly elastic waistband and retro-inspired silhouette.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthRibbedHighWaistRose.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "high-waist", "baseLooks": 4, "price": 15, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_minimal_thong_sage", "name": "Sage Green Minimal String Thong", "brand": "Northline Apparel", "desc": "Muted sage green minimal string thong crafted from smooth stretch microfiber, invisible lines guaranteed.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthMinimalThongSage.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "thong", "baseLooks": 3, "price": 10, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_lace_trim_hipster_white", "name": "White Lace-Trim Cotton Hipster", "brand": "Northline Apparel", "desc": "Soft white cotton hipster panty with delicate floral lace trim along the leg openings for a subtle feminine touch.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthLaceTrimHipsterWhite.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "hipster", "baseLooks": 3, "price": 15, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_seamless_thong_navy", "name": "Navy Seamless Microfiber Thong", "brand": "Northline Apparel", "desc": "Deep navy seamless microfiber thong that sits low on the hips and leaves no visible lines whatsoever.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthSeamlessThongNavy.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "thong", "baseLooks": 3, "price": 15, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_north_high_waist_brief_black", "name": "Black High-Waist Smoothing Brief", "brand": "Northline Apparel", "desc": "Black high-waist smoothing brief made from structured stretch-cotton, providing gentle compression and a defined silhouette.", "image": "assets/content/clothing/panties/NorthlineApparel/pantyNorthHighWaistBriefBlack.webp", "store": "storeClothingA", "slot": "panty", "silhouette": "high-waist", "baseLooks": 3, "price": 15, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_ribbon_satin_blush_set", "name": "Dusty Blush Satin Ribbon-Tie Bikini", "brand": "Silk & Lace", "desc": "Soft dusty blush satin bikini panty with delicate ribbon-tie sides and a gentle lace-trim edge. Part of the silkBlushRibbonSet.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLRibbonSatinBlushSet.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "bikini-cut", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": "silkBlushRibbonSet", "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_satin_coral_set", "name": "Coral Satin Scallop High-Waist", "brand": "Silk & Lace", "desc": "Coral satin high-waist panty with wide scalloped lace trim along the leg openings and a small center bow. Part of the silkCoralFloralSet.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLSatinCoralSet.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "high-waist", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 1, "matchSet": "silkCoralFloralSet", "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_satin_cheeky_ivory", "name": "Ivory Satin Cheeky Panty", "brand": "Silk & Lace", "desc": "Smooth ivory satin cheeky panty with a ruched back seam to enhance the silhouette.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLSatinCheekyIvory.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "cheeky", "baseLooks": 3, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_lace_brief_dusty_mauve", "name": "Dusty Mauve Lace Brief", "brand": "Silk & Lace", "desc": "Dusty mauve all-over lace brief with scalloped edges and a comfortable yet elegant full coverage.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLLaceBriefDustyMauve.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "brief", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_satin_thong_navy", "name": "Navy Satin Thong", "brand": "Silk & Lace", "desc": "Deep navy satin thong with a thin delicate waistband and lace embellishment at the front hips.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLSatinThongNavy.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "thong", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_lace_thong_cream_floral", "name": "Cream Floral Lace Thong", "brand": "Silk & Lace", "desc": "Cream floral lace thong with delicate scalloped edges and a very narrow back string.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLLaceThongCreamFloral.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "thong", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_satin_bikini_black", "name": "Black Satin Lace-Panel Bikini", "brand": "Silk & Lace", "desc": "Deep black satin bikini panty with sheer lace side panels and a delicate bow at the center front.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLSatinBikiniBlack.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "bikini-cut", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 35, "reqHeelsSkill": 0},
{"id": "panty_sl_strappy_thong_emerald", "name": "Emerald Lace Strappy Thong", "brand": "Silk & Lace", "desc": "Rich emerald green lace thong featuring an intricate strappy waistband detail and gold hardware accents.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLStrappyThongEmerald.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "thong", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 3, "matchSet": "silkEmeraldStrappySet", "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_sheer_mesh_cheeky_burgundy", "name": "Burgundy Sheer Mesh Cheeky", "brand": "Silk & Lace", "desc": "Deep burgundy sheer mesh cheeky panty with lace trim along the edges, offering a teasing semi-transparent look.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLSheerMeshCheekyBurgundy.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "cheeky", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 35, "reqHeelsSkill": 0},
{"id": "panty_sl_rose_gold_satin_thong", "name": "Rose Gold Satin Micro Thong", "brand": "Silk & Lace", "desc": "Luxurious rose gold satin micro thong featuring super thin side straps and a minimal front triangle. Premium exposure.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLRoseGoldSatinThong.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "thong", "baseLooks": 6, "price": 35, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_cotton_brief_white", "name": "White Cotton Soft-Trim Brief", "brand": "Silk & Lace", "desc": "Clean white cotton high-quality brief with a delicate scalloped edge and tiny front bow.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLCottonBriefWhite.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "brief", "baseLooks": 3, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_lace_thong_lavender", "name": "Lavender Lace Thong", "brand": "Silk & Lace", "desc": "Soft lavender all-over lace thong featuring a wide lace waistband that sits flat against the skin.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLLaceThongLavender.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "thong", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_satin_high_waist_forest_green", "name": "Forest Green Satin High-Waist", "brand": "Silk & Lace", "desc": "Rich forest green satin high-waist panty with deep V back cutout and a delicate button closure detail.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLSatinHighWaistForestGreen.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "high-waist", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 100, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_sheer_mesh_thong_peach", "name": "Peach Sheer Mesh Thong", "brand": "Silk & Lace", "desc": "Soft peach sheer mesh thong with a thin elasticated waist string and scalloped lace trim.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLSheerMeshThongPeach.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "thong", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 65, "reqExhibitionism": 35, "reqHeelsSkill": 0},
{"id": "panty_sl_premium_lace_thong_wine_red", "name": "Wine Red Guipure Lace Thong", "brand": "Silk & Lace", "desc": "Premium wine red guipure lace thong with intricate heavy lace detailing and delicate satin side straps.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLPremiumLaceThongWineRed.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "thong", "baseLooks": 6, "price": 40, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {"elite": 6}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_sl_premium_embroidered_cheeky_charcoal", "name": "Charcoal Sheer Embroidered Cheeky", "brand": "Silk & Lace", "desc": "Dark charcoal sheer tulle cheeky panty featuring exquisite hand-embroidered floral motifs that appear to float on the skin.", "image": "assets/content/clothing/panties/SilkAndLace/pantySLPremiumEmbroideredCheekyCharcoal.webp", "store": "storeLingerieA", "slot": "panty", "silhouette": "cheeky", "baseLooks": 7, "price": 45, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {"elite": 8}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 35, "reqHeelsSkill": 0},
{"id": "panty_is_lace_thong_purple_t3", "name": "Deep Violet Lace Thong", "brand": "Intimate Secrets", "desc": "Deep violet sheer lace thong with a scalloped edge and thin elastic side strings.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISLaceThongPurpleT3.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 4, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 100, "reqCorruption": 3, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_sheer_mesh_cheeky_red_t3", "name": "Crimson Sheer Mesh Cheeky", "brand": "Intimate Secrets", "desc": "Crimson red completely sheer mesh cheeky panty with elasticated edges and a teasing sheer aesthetic.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISSheerMeshCheekyRedT3.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "cheeky", "baseLooks": 4, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 100, "reqCorruption": 4, "matchSet": "intimateRedLaceSet", "tags": ["sexy", "daring", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_strappy_cage_black_mesh_t4", "name": "Black Strappy Cage Mesh Cheeky", "brand": "Intimate Secrets", "desc": "Deep matte black sheer mesh cheeky panty enhanced with adjustable strappy cage details at the hips. Part of the intimateBlackMeshSet.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISStrappyCageBlackMeshT4.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "cheeky", "baseLooks": 5, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 5, "matchSet": "intimateBlackMeshSet", "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_sheer_lace_thong_night_blue_t4", "name": "Midnight Navy Sheer Lace Thong", "brand": "Intimate Secrets", "desc": "Midnight navy sheer lace thong cut extremely high on the leg, featuring a teasing strappy detail at the waist.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISSheerLaceThongNightBlueT4.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 5, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 5, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_harness_strap_black_satin_t4", "name": "Black Satin Harness-Strap Thong", "brand": "Intimate Secrets", "desc": "Intricate black satin thong constructed mostly of elaborate harness-style straps crossing over the hips and pelvis.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISHarnessStrapBlackSatinT4.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 5, "price": 55, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_snake_print_mesh_thong_t4", "name": "Snake Print Mesh Thong", "brand": "Intimate Secrets", "desc": "Tonal grey snake print sheer mesh thong with high-cut strappy sides and gold hardware accents. Part of the intimateSnakeMeshSet.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISSnakePrintMeshThongT4.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 5, "price": 50, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 100, "reqCorruption": 5, "matchSet": "intimateSnakeMeshSet", "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 70, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_barely_there_micro_red_t5", "name": "Red Barely-There String Thong", "brand": "Intimate Secrets", "desc": "Deep red ultra-minimal string micro thong that provides barely any coverage at all. Maximum exposure.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISBarelyThereMicroRedT5.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 5, "price": 55, "quality": "Rare", "warmth": 0, "sexinessScore": 9, "exposureLevel": 9, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_open_crotch_lace_black_t5", "name": "Black Lace Open-Crotch Thong", "brand": "Intimate Secrets", "desc": "Matte black all-over lace thong with a fully open crotch design bordered by scalloped lace trim and thin side straps.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISOpenCrotchLaceBlackT5.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 5, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "lewd", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 95, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_wet_look_thong_black_t4", "name": "Black Wet-Look Strappy Thong", "brand": "Intimate Secrets", "desc": "Glossy black wet-look faux leather thong with aggressive high-cut side straps and silver hardware.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISWetLookThongBlackT4.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 6, "price": 65, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["revealing", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 85, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_sheer_lace_red_harness_t5", "name": "Red Sheer Lace Harness Thong", "brand": "Intimate Secrets", "desc": "Deep red sheer lace thong layered underneath an elaborate strappy harness overlay with gold O-ring hardware.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISSheerLaceRedHarnessT5.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 6, "price": 75, "quality": "Premium", "warmth": 0, "sexinessScore": 9, "exposureLevel": 9, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 90, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "panty_is_crotchless_body_chain_gold_black_t5", "name": "Black Lace Crotchless Body-Chain Thong", "brand": "Intimate Secrets", "desc": "Barely-there black lace open-crotch bottom piece suspended solely by an elaborate delicate gold body chain harness crossing the hips.", "image": "assets/content/clothing/panties/IntimateSecrets/pantyISCrotchlessBodyChainGoldBlackT5.webp", "store": "storeLingerieB", "slot": "panty", "silhouette": "thong", "baseLooks": 6, "price": 85, "quality": "Premium", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 100, "reqCorruption": 6, "matchSet": null, "tags": ["daring", "erotic", "lewd", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 100, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* Player Wardrobe State
* This initializes the player's wardrobe state at game start
*
* $wardrobe.equipped - Currently worn items { slot: itemId }
* $wardrobe.outfits - Saved outfit combinations
* $wardrobe.owned - Array of owned item IDs
* $wardrobe.itemState - Runtime item state { [id]: { durability, dirt } }
* $wardrobe.wornToday - Minutes worn today by item { [id]: minutes }
* $wardrobe.laundryBasket - Array of item IDs ready for laundry (IDs added only while $location is fhBedroom)
*/
/* Initialize wardrobe state */
<<set $wardrobe = {
equipped: {},
outfits: [null, null, null, null, null],
owned: [],
itemState: {},
wornToday: {},
laundryBasket: []
}>>
/* Note: initialization logic is now handled lazily by the macro
to ensure all clothing data passages are loaded first. *//* rings DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.rings = [
{"id": "jewel_rg_solitaire_princess_crystal_t4", "name": "Princess Solitaire Ring", "brand": "Diamond Dreams", "desc": "A flawless, breathtakingly large square princess-cut clear cubic zirconia. Set magnificently high on a thin sterling silver band.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGSolitairePrincessCrystalT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 9, "price": 420, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_rg_stacking_bands_gold_t2", "name": "Gold Stacking Rings", "brand": "Diamond Dreams", "desc": "A popular everyday casual set of three incredibly thin, lightly hammered 18k gold bands worn stacked tightly together on a single finger.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGStackingBandsGoldT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 6, "price": 365, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_rg_signet_engraved_silver_t3", "name": "Engraved Silver Signet", "brand": "Diamond Dreams", "desc": "A thick, heavy traditional silver signet ring with a completely flat, highly polished circular face. Looks deeply historical and unapologetically heavy.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGSignetEngravedSilverT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 7, "price": 430, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_rg_cocktail_morganite_rose_t4", "name": "Pink Morganite Jewel Ring", "brand": "Diamond Dreams", "desc": "A stunningly romantic massive oval-cut pale pink morganite jewel, flanked heavily by tiny pave crystals on a rose gold band.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGCocktailMorganiteRoseT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 9, "price": 390, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal", "cute"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_rg_eternity_band_crystal_silver_t3", "name": "Crystal Eternity Band", "brand": "Diamond Dreams", "desc": "A flawlessly continuous band completely encircled by deeply set rectangular cubic zirconia baguettes creating a perfect ring of light.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGEternityBandCrystalSilverT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 8, "price": 360, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_rg_bypass_pearl_crystal_gold_t3", "name": "Bypass Pearl Ring", "brand": "Diamond Dreams", "desc": "An elegant open-ended \"bypass\" ring. One end terminates in a perfectly round white pearl, deeply contrasted opposite a brilliant square-cut crystal on the other end.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGBypassPearlCrystalGoldT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 7, "price": 335, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_rg_mini_chain_link_silver_t2", "name": "Silver Chain Link Ring", "brand": "Diamond Dreams", "desc": "Industrial flexibility. An incredibly unusual ring fashioned completely out of a small, flexible loop of interlocking silver curb chain rather than a solid metallic band.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGMiniChainLinkSilverT2.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 6, "price": 310, "quality": "Rare", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_rg_sapphire_trillion_gold_t4", "name": "Trillion Sapphire Ring", "brand": "Diamond Dreams", "desc": "A sharp, uniquely triangular trillion-cut deep blue sapphire center stone aggressively framed in wide, polished yellow gold.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGSapphireTrillionGoldT4.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 9, "price": 440, "quality": "Luxury", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "jewel_rg_hammered_dome_rose_t3", "name": "Rose Gold Domed Ring", "brand": "Diamond Dreams", "desc": "A substantial, incredibly wide, convexly domed rose gold ring. The entire curving face is thoroughly dimpled with organic hammered texture.", "image": "assets/content/clothing/jewelry/DiamondDreams/jewelRGHammeredDomeRoseT3.webp", "store": "storeJewelry", "slot": "jewelry", "silhouette": "ring", "baseLooks": 7, "price": 405, "quality": "Premium", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* shoes DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.shoes = [
{"id":"sneakers_flat_owned","name":"Everyday Flat Sneakers","brand":"","desc":"Comfortable flat sneakers for everyday wear.","image":"assets/content/clothing/shoes/Starter/sneakers_flat_owned.webp","store":"","slot":"shoes","silhouette":"sneakers","baseLooks":1,"price":0,"quality":"Common","warmth":0,"sexinessScore":0,"exposureLevel":0,"durability":100,"reqCorruption":0,"reqHeelsSkill":0,"matchSet":null,"tags":["casual","basic","mild"],"npcAppeal":{},"shopAvailable":false,"startOwned":true,"reqConfidence":0},
{"id":"sneakers_girly_owned","name":"Everyday Girly Sneakers","brand":"","desc":"Simple girly sneakers for everyday wear.","image":"assets/content/clothing/shoes/Starter/sneakers_girly_owned.webp","store":"","slot":"shoes","silhouette":"sneakers","baseLooks":1,"price":0,"quality":"Common","warmth":0,"sexinessScore":0,"exposureLevel":0,"durability":100,"reqCorruption":0,"reqHeelsSkill":0,"matchSet":null,"tags":["casual","basic","sporty"],"npcAppeal":{},"shopAvailable":false,"startOwned":true,"reqConfidence":0},
{"id": "shoes_su_chunky_platform_sneaker_white_t1", "name": "White Chunky Sneakers", "brand": "StepUp", "desc": "Trendy, oversized white chunky platform sneakers. Comfortable for all-day walking while adding a bit of height.", "image": "assets/content/clothing/shoes/StepUp/shoesSUChunkyPlatformSneakerWhiteT1.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 45, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_canvas_flat_floral_t1", "name": "Floral Canvas Flats", "brand": "StepUp", "desc": "Simple, lightweight canvas slip-on flats featuring a cute colorful floral print. Perfect for a quick summer outing.", "image": "assets/content/clothing/shoes/StepUp/shoesSUCanvasFlatFloralT1.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "flat", "baseLooks": 2, "price": 25, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_leopard_loafer_t2", "name": "Leopard Print Loafers", "brand": "StepUp", "desc": "Smart-casual structured loafers wrapped in a bold leopard print faux-suede. A stylish step above basic flats.", "image": "assets/content/clothing/shoes/StepUp/shoesSULeopardLoaferT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "loafer", "baseLooks": 4, "price": 60, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_color_block_sandal_t0", "name": "Color-Block Sport Sandals", "brand": "StepUp", "desc": "Simple velcro-strap open sandals featuring bright teal, yellow, and red color-blocked straps on a foam sole.", "image": "assets/content/clothing/shoes/StepUp/shoesSUColorBlockSandalT0.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sandal", "baseLooks": 2, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_chelsea_boot_black_t2", "name": "Classic Black Chelsea Boots", "brand": "StepUp", "desc": "Timeless ankle-high Chelsea boots in smooth black matte leather with elastic side panels. An essential piece for cooler weather.", "image": "assets/content/clothing/shoes/StepUp/shoesSUChelseaBootBlackT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "boot", "baseLooks": 3, "price": 75, "quality": "Rare", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_espadrille_wedge_tan_t2", "name": "Tan Espadrille Wedges", "brand": "StepUp", "desc": "Summer-ready wedge sandals featuring a natural woven jute rope sole, soft tan suede toe cover, and straps tying around the ankle.", "image": "assets/content/clothing/shoes/StepUp/shoesSUEspadrilleWedgeTanT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "wedge", "baseLooks": 4, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 1, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0},
{"id": "shoes_su_slip_on_sneaker_grey_t0", "name": "Grey Slip-On Sneakers", "brand": "StepUp", "desc": "Super simple, laceless grey canvas slip-on sneakers with a white rubber sole. The ultimate lazy day footwear.", "image": "assets/content/clothing/shoes/StepUp/shoesSUSlipOnSneakerGreyT0.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 1, "price": 30, "quality": "Common", "warmth": 2, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_strappy_flat_sandal_t1", "name": "Strappy Flat Sandals", "brand": "StepUp", "desc": "Delicate, flat summer sandals with multiple thin black faux-leather straps cris-crossing over the foot.", "image": "assets/content/clothing/shoes/StepUp/shoesSUStrappyFlatSandalT1.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sandal", "baseLooks": 2, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "shoes_su_ankle_boot_suede_t2", "name": "Suede Ankle Boots", "brand": "StepUp", "desc": "Soft slouchy ankle boots fashioned from caramel brown faux suede. A gentle low heel provides a comfortable boost.", "image": "assets/content/clothing/shoes/StepUp/shoesSUAnkleBootSuedeT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "bootie", "baseLooks": 3, "price": 80, "quality": "Rare", "warmth": 3, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 1, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0},
{"id": "shoes_su_canvas_high_top_black_t1", "name": "Black Canvas High-Tops", "brand": "StepUp", "desc": "Classic retro canvas high-top sneakers in solid black with a bold white rubber toe cap and thick white laces.", "image": "assets/content/clothing/shoes/StepUp/shoesSUCanvasHighTopBlackT1.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 2, "price": 35, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_rubber_rain_boot_yellow_t2", "name": "Yellow Rubber Rain Boots", "brand": "StepUp", "desc": "Cheerful, bright yellow waterproof rubber ankle boots perfectly designed for splashing through puddles on rainy days.", "image": "assets/content/clothing/shoes/StepUp/shoesSURubberRainBootYellowT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "boot", "baseLooks": 3, "price": 40, "quality": "Common", "warmth": 3, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_gladiator_flat_sandal_tan_t1", "name": "Tan Gladiator Sandals", "brand": "StepUp", "desc": "Strappy flat gladiator-style sandals crafted from genuine tan suede leather, featuring multiple horizontal straps hugging the foot.", "image": "assets/content/clothing/shoes/StepUp/shoesSUGladiatorFlatSandalTanT1.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sandal", "baseLooks": 3, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_fuzzy_house_slipper_pink_t0", "name": "Pink Fuzzy Slippers", "brand": "StepUp", "desc": "Incredibly soft, plush pink faux-fur open-toe house slippers. Essentially walking on clouds inside your home.", "image": "assets/content/clothing/shoes/StepUp/shoesSUFuzzyHouseSlipperPinkT0.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "slipper", "baseLooks": 2, "price": 20, "quality": "Common", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_slip_on_mule_black_t2", "name": "Black Leather Mules", "brand": "StepUp", "desc": "A sleek, backless black leather slip-on mule flat. Provides a polished look with the ease of a slipper.", "image": "assets/content/clothing/shoes/StepUp/shoesSUSlipOnMuleBlackT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "mule", "baseLooks": 3, "price": 50, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_wedge_sneaker_white_t1", "name": "White Wedge Sneakers", "brand": "StepUp", "desc": "Sporty, casual white high-top sneakers with a cleverly hidden wedge heel inside for an invisible height boost.", "image": "assets/content/clothing/shoes/StepUp/shoesSUWedgeSneakerWhiteT1.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 60, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "reqHeelsSkill": 1, "matchSet": null, "tags": ["casual", "sporty", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0},
{"id": "shoes_su_mary_jane_patent_black_t2", "name": "Patent Black Mary Janes", "brand": "StepUp", "desc": "Timeless, cute Mary Jane flat shoes crafted from highly glossy black patent leather with a single silver-buckle strap.", "image": "assets/content/clothing/shoes/StepUp/shoesSUMaryJanePatentBlackT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "flat", "baseLooks": 3, "price": 45, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_combat_boot_black_t2", "name": "Chunky Combat Boots", "brand": "StepUp", "desc": "Edgy and tough mid-calf black leather combat boots featuring a thick rugged sole and heavy laces pulling tight.", "image": "assets/content/clothing/shoes/StepUp/shoesSUCombatBootBlackT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "boot", "baseLooks": 4, "price": 85, "quality": "Rare", "warmth": 3, "sexinessScore": 2, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_cork_slide_sandal_brown_t0", "name": "Cork Slide Sandals", "brand": "StepUp", "desc": "Timelessly simple contoured cork footbed slides with a double-buckle wide brown suede strap over the foot.", "image": "assets/content/clothing/shoes/StepUp/shoesSUCorkSlideSandalBrownT0.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sandal", "baseLooks": 2, "price": 40, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_kitten_heel_pump_nude_t2", "name": "Nude Kitten Heels", "brand": "StepUp", "desc": "A conservative, modest closed-toe pump with a tiny 5cm kitten heel in a versatile polished nude faux-leather.", "image": "assets/content/clothing/shoes/StepUp/shoesSUKittenHeelPumpNudeT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "pump", "baseLooks": 3, "price": 50, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 1, "matchSet": null, "tags": ["casual", "professional", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0},
{"id": "shoes_su_slouchy_knee_boot_taupe_t2", "name": "Slouchy Knee Boots", "brand": "StepUp", "desc": "Tall, relaxed-fit knee-high boots made from incredibly soft draped taupe suede. A stylish winter essential.", "image": "assets/content/clothing/shoes/StepUp/shoesSUSlouchyKneeBootTaupeT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "boot", "baseLooks": 4, "price": 90, "quality": "Rare", "warmth": 4, "sexinessScore": 3, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "reqHeelsSkill": 2, "matchSet": null, "tags": ["casual", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "shoes_su_platform_oxford_burgundy_t2", "name": "Burgundy Platform Oxfords", "brand": "StepUp", "desc": "A preppy classic upgraded with serious attitude. Dark burgundy polished leather oxfords sitting on a massive chunky black rubber sole.", "image": "assets/content/clothing/shoes/StepUp/shoesSUPlatformOxfordBurgundyT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "oxford", "baseLooks": 4, "price": 70, "quality": "Rare", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "professional", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_retro_sneaker_pastel_t1", "name": "Pastel Retro Sneakers", "brand": "StepUp", "desc": "Playful vintage-inspired low-top sneakers featuring blocks of soft pastel pink, baby blue, and mint green suede.", "image": "assets/content/clothing/shoes/StepUp/shoesSURetroSneakerPastelT1.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 55, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_clear_jelly_sandal_pink_t0", "name": "Pink Jelly Sandals", "brand": "StepUp", "desc": "Childhood nostalgia brought back. Completely transparent, flexible pink PVC jelly sandals featuring a woven fisherman cage structure.", "image": "assets/content/clothing/shoes/StepUp/shoesSUClearJellySandalPinkT0.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sandal", "baseLooks": 2, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_hiker_boot_tan_t2", "name": "Tan Hiking Boots", "brand": "StepUp", "desc": "Sturdy, outdoor-ready fashion hiking boots wrapped in thick tan suede, featuring aggressive tread and thick bright red round laces looped through D-rings.", "image": "assets/content/clothing/shoes/StepUp/shoesSUHikerBootTanT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "boot", "baseLooks": 3, "price": 75, "quality": "Rare", "warmth": 4, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_woven_mule_brown_t2", "name": "Woven Leather Mules", "brand": "StepUp", "desc": "Hand-crafted slip-on flat mules displaying an intricate basket-weave of warm brown leather straps crossing over the toe.", "image": "assets/content/clothing/shoes/StepUp/shoesSUWovenMuleBrownT2.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "mule", "baseLooks": 3, "price": 65, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0},
{"id": "shoes_su_court_sneaker_white_t1", "name": "Classic White Sneakers", "brand": "StepUp", "desc": "A timeless wardrobe staple. Spotless minimalist low-profile white faux-leather tennis court sneakers with matching white flat laces.", "image": "assets/content/clothing/shoes/StepUp/shoesSUCourtSneakerWhiteT1.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 45, "quality": "Common", "warmth": 2, "sexinessScore": 0, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "professional", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_su_block_ankle_boot_snake_t3", "name": "Snakeskin Block Booties", "brand": "StepUp", "desc": "Fierce ankle booties wrapped in a highly-textured grey and black python snake print faux-leather, elevated by a sharp, architectural chunky black block heel.", "image": "assets/content/clothing/shoes/StepUp/shoesSUBlockAnkleBootSnakeT3.webp", "store": "storeShoeA", "slot": "shoes", "silhouette": "bootie", "baseLooks": 4, "price": 85, "quality": "Premium", "warmth": 2, "sexinessScore": 3, "exposureLevel": 0, "durability": 75, "reqCorruption": 0, "reqHeelsSkill": 2, "matchSet": null, "tags": ["casual", "sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "shoes_ss_classic_pump_patent_black_t3", "name": "Classic Black Stilettos", "brand": "Stiletto", "desc": "The ultimate timeless high heel. A sleek incredibly sharp pointed-toe pump rendered entirely in severely glossy black patent leather balancing effortlessly on a thin 10cm stiletto spike.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSClassicPumpPatentBlackT3.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 4, "price": 95, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["elegant", "sexy", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "shoes_ss_ankle_strap_block_sandal_nude_t2", "name": "Nude Block Sandals", "brand": "Stiletto", "desc": "A versatile nude suede open-toe sandal featuring an elegant slim ankle strap securely buckled over a thick, stable 8cm block heel.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSAnkleStrapBlockSandalNudeT2.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "sandal", "baseLooks": 3, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "reqHeelsSkill": 2, "matchSet": null, "tags": ["cute", "elegant", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "shoes_ss_clear_pvc_mule_lucite_t4", "name": "Clear Lucite Mules", "brand": "Stiletto", "desc": "Completely transparent, illusion-like backless slip-on mules featuring an invisible wide PVC plastic vamp and a solid glass-like 10cm lucite block heel.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSClearPvcMuleLuciteT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "mule", "baseLooks": 5, "price": 105, "quality": "Premium", "warmth": 0, "sexinessScore": 4, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0},
{"id": "shoes_ss_extreme_platform_red_t5", "name": "Extreme Red Platforms", "brand": "Stiletto", "desc": "A purely erotic statement piece. Enormous 15cm sky-high red patent stilettos towering precariously over an unapologetically massive 5cm front platform.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSExtremePlatformRedT5.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "sandal", "baseLooks": 6, "price": 140, "quality": "Luxury", "warmth": 0, "sexinessScore": 5, "exposureLevel": 0, "durability": 60, "reqCorruption": 4, "reqHeelsSkill": 4, "matchSet": null, "tags": ["sexy", "daring", "slutty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0},
{"id": "shoes_ss_architectural_gold_t4", "name": "Gold Sculptural Heels", "brand": "Stiletto", "desc": "An avant-garde masterpiece of footwear. The heel itself is cast in twisted, surreal modern art shapes entirely glowing in bright metallic gold.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSArchitecturalGoldT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 5, "price": 180, "quality": "Premium", "warmth": 1, "sexinessScore": 3, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 4, "matchSet": null, "tags": ["elegant", "sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0},
{"id": "shoes_ss_gladiator_thigh_high_black_t4", "name": "Tall Gladiator Heels", "brand": "Stiletto", "desc": "Fierce, commanding black leather gladiator heels wrapped endlessly with dozens of thin leather cord ties crisscrossing violently entirely up to the bare knee.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSGladiatorThighHighBlackT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "sandal", "baseLooks": 5, "price": 160, "quality": "Premium", "warmth": 1, "sexinessScore": 4, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0},
{"id": "shoes_ss_crystal_bow_pump_silver_t3", "name": "Crystal Bow Satin Pumps", "brand": "Stiletto", "desc": "A fairytale slipper brought to life. Soft shimmering silver satin pumps adorned beautifully with a giant bedazzled rhinestone crystal bow squarely over the toe box.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSCrystalBowPumpSilverT3.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 4, "price": 130, "quality": "Premium", "warmth": 1, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 2, "matchSet": null, "tags": ["elegant", "cute", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "shoes_ss_leopard_bootie_t3", "name": "Leopard Stiletto Booties", "brand": "Stiletto", "desc": "Dangerously sexy ankle boots wrapped fiercely in a heavily textured exotic leopard print faux fur, completely enclosing the foot above a sharp 10cm spike.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSLeopardBootieT3.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "bootie", "baseLooks": 4, "price": 120, "quality": "Premium", "warmth": 3, "sexinessScore": 3, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["sexy", "daring", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "shoes_ss_slouchy_over_knee_boot_black_t4", "name": "Over-Knee Suede Boots", "brand": "Stiletto", "desc": "Incredibly tall, dramatic slouchy black suede boots that reach provocatively all the way past the knees, resting on a sharp 10cm stiletto heel.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSSlouchyOverKneeBootBlackT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "boot", "baseLooks": 6, "price": 190, "quality": "Premium", "warmth": 4, "sexinessScore": 4, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["elegant", "sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0},
{"id": "shoes_ss_peep_toe_platform_nude_t4", "name": "Nude Peep-Toe Platforms", "brand": "Stiletto", "desc": "Towering 12cm sky-high nude faux-patent pumps with a thick concealed platform at the front and a classic flirty peep-toe cutout exposing the painted toes.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSPeepToePlatformNudeT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 4, "price": 115, "quality": "Premium", "warmth": 1, "sexinessScore": 3, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 4, "matchSet": null, "tags": ["elegant", "sexy", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "shoes_ss_kitten_slingback_white_t2", "name": "White Slingback Heels", "brand": "Stiletto", "desc": "Graceful 5cm kitten heel pumps draped flawlessly in pure soft white leather, securing elegantly with a thin buckled strap behind the bare heel.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSKittenSlingbackWhiteT2.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 3, "price": 85, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 1, "matchSet": null, "tags": ["cute", "elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0},
{"id": "shoes_ss_strappy_wrap_neon_pink_t4", "name": "Neon Pink Lace-Up Heels", "brand": "Stiletto", "desc": "Wildly intense, dangerously high 12cm hot pink stiletto sandals constructed from absurdly long thin leather stringties wrapping seductively around the bare calves repeatedly.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSStrappyWrapNeonPinkT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "sandal", "baseLooks": 5, "price": 145, "quality": "Premium", "warmth": 0, "sexinessScore": 4, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 4, "matchSet": null, "tags": ["sexy", "daring", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0},
{"id": "shoes_ss_mirror_chrome_pump_silver_t4", "name": "Chrome Mirror Pumps", "brand": "Stiletto", "desc": "A futuristic weapon of seduction. Closed-toe stiletto pumps entirely submerged in a blindingly reflective liquid-silver chrome mirror finish.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSMirrorChromePumpSilverT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 5, "price": 125, "quality": "Premium", "warmth": 1, "sexinessScore": 3, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 4, "matchSet": null, "tags": ["sexy", "daring", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0},
{"id": "shoes_ss_snakeskin_stiletto_top_green_t3", "name": "Green Snakeskin Booties", "brand": "Stiletto", "desc": "Ruthless, pointed-toe zip-up stiletto ankle boots carefully upholstered in highly detailed bright emerald green and black python snakeskin print.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSSnakeskinStilettoTopGreenT3.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "bootie", "baseLooks": 4, "price": 105, "quality": "Premium", "warmth": 2, "sexinessScore": 3, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["sexy", "daring", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "shoes_ss_minimalist_two_strap_black_t3", "name": "Minimalist Black Sandals", "brand": "Stiletto", "desc": "The epitome of understated elegance. A delicate black stiletto sandal with just two whisper-thin black straps holding the entire foot in place.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSMinimalistTwoStrapBlackT3.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "sandal", "baseLooks": 4, "price": 110, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 2, "matchSet": null, "tags": ["elegant", "sexy", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0},
{"id": "shoes_ss_stripper_pole_clear_extreme_t5", "name": "Extreme Pleaser Platforms", "brand": "Stiletto", "desc": "Shamelessly towering exotic dancer shoes. Built on an absurdly thick 8cm clear lucite platform block dropping violently onto a shocking 18cm sky-high stiletto.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSStripperPoleClearExtremeT5.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "sandal", "baseLooks": 6, "price": 150, "quality": "Luxury", "warmth": 0, "sexinessScore": 5, "exposureLevel": 0, "durability": 60, "reqCorruption": 4, "reqHeelsSkill": 4, "matchSet": null, "tags": ["daring", "slutty", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0},
{"id": "shoes_ss_caged_lace_up_heel_black_t4", "name": "Caged Black Lace-Ups", "brand": "Stiletto", "desc": "A provocative, dominant black leather stiletto totally engulfing the foot in a fierce, structured cage of intricate laser-cut straps tied securely at the ankle.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSCagedLaceUpHeelBlackT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "sandal", "baseLooks": 5, "price": 135, "quality": "Premium", "warmth": 1, "sexinessScore": 4, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["sexy", "daring", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0},
{"id": "shoes_ss_pointed_slingback_red_t3", "name": "Red Pointed Slingbacks", "brand": "Stiletto", "desc": "Strikingly bright cherry-red suede pumps featuring an exceedingly sharp pointed toe and a thin slingback strap wrapping behind the exposed heel.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSPointedSlingbackRedT3.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 4, "price": 110, "quality": "Premium", "warmth": 1, "sexinessScore": 3, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 2, "matchSet": null, "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "shoes_ss_t_strap_pump_black_white_t3", "name": "Two-Tone T-Strap Pumps", "brand": "Stiletto", "desc": "A chic, vintage-inspired T-strap high heel featuring a stunning contrast of stark white leather completely outlined by bold black trim directly down the center.", "image": "assets/content/clothing/shoes/Stiletto/shoesSST-StrapPumpBlackWhiteT3.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 4, "price": 105, "quality": "Premium", "warmth": 1, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 2, "matchSet": null, "tags": ["elegant", "professional", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "shoes_ss_studded_ankle_boot_black_t4", "name": "Studded Stiletto Booties", "brand": "Stiletto", "desc": "Aggressive black leather high heel booties fiercely lined forcefully entirely everywhere with intimidating sharp silver metallic spikes.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSStuddedAnkleBootBlackT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "bootie", "baseLooks": 5, "price": 140, "quality": "Premium", "warmth": 2, "sexinessScore": 4, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["daring", "sexy", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0},
{"id": "shoes_ss_feather_mule_pink_t3", "name": "Pink Feather Marabou Heels", "brand": "Stiletto", "desc": "A total boudoir fantasy. Slip-on pink silk mule sandals featuring a toe strap entirely covered in fluffy pink marabou feathers.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSFeatherMulePinkT3.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "mule", "baseLooks": 5, "price": 110, "quality": "Premium", "warmth": 0, "sexinessScore": 4, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["sexy", "elegant", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0},
{"id": "shoes_ss_velvet_platform_pump_navy_t4", "name": "Navy Velvet Platforms", "brand": "Stiletto", "desc": "Ultra-tall stiletto platform pumps upholstered flawlessly in rich, dark navy-blue velvet, built on a concealed front platform and an imposing thick 12cm block heel.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSVelvetPlatformPumpNavyT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "pump", "baseLooks": 4, "price": 130, "quality": "Premium", "warmth": 2, "sexinessScore": 3, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 4, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "shoes_ss_corset_bootie_lace_white_t4", "name": "White Lace Corset Booties", "brand": "Stiletto", "desc": "Bridal elegance meets dominatrix. A stunning 11cm white high heel ankle boot wrapped in delicate semi-sheer white floral lace and structured with corset-style lacing up the front.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSCorsetBootieLaceWhiteT4.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "bootie", "baseLooks": 5, "price": 155, "quality": "Premium", "warmth": 2, "sexinessScore": 4, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 3, "matchSet": null, "tags": ["elegant", "sexy", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0},
{"id": "shoes_ss_thigh_high_stiletto_vinyl_red_t5", "name": "Red Vinyl Thigh-Highs", "brand": "Stiletto", "desc": "The ultimate fetish statement. Impossibly long, blindingly glossy bright red tight vinyl boots pulling aggressively over the thighs to act like a second skin, atop a wicked 12cm metal spike.", "image": "assets/content/clothing/shoes/Stiletto/shoesSSThighHighStilettoVinylRedT5.webp", "store": "storeShoeB", "slot": "shoes", "silhouette": "boot", "baseLooks": 6, "price": 195, "quality": "Luxury", "warmth": 4, "sexinessScore": 5, "exposureLevel": 0, "durability": 65, "reqCorruption": 3, "reqHeelsSkill": 4, "matchSet": null, "tags": ["slutty", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0},
{"id": "shoes_fb_pro_running_sneaker_neon_t2", "name": "Neon Pro Running Shoes", "brand": "FastBreak", "desc": "Ultra-lightweight mesh performance running shoes equipped with high-energy return cushioning in aggressive neon green and black.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBProRunningSneakerNeonT2.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 95, "quality": "Rare", "warmth": 2, "sexinessScore": 0, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_crossfit_trainer_grey_pink_t1", "name": "CrossFit Training Shoes", "brand": "FastBreak", "desc": "Stable, flat-soled grey and pastel pink training shoes built tough to withstand rope climbs, box jumps, and heavy gym lifting.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBCrossfitTrainerGreyPinkT1.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 2, "price": 80, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_basketball_high_top_red_white_t2", "name": "Red/White BBall High-Tops", "brand": "FastBreak", "desc": "Classic court-inspired leather high-top basketball sneakers color-blocked in deep red and crisp white panels. Heavy padding around the ankles.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBBasketballHighTopRedWhiteT2.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 110, "quality": "Rare", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 75, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0},
{"id": "shoes_fb_recovery_slide_black_t0", "name": "Athletic Recovery Slides", "brand": "FastBreak", "desc": "A single thick heavily-cushioned strap holding onto incredibly soft shock-absorbing black foam, designed strictly for giving athletes' feet a rest after huge games.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBRecoverySlideBlackT0.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sandal", "baseLooks": 1, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_barefoot_trail_runner_olive_t2", "name": "Barefoot Trail Runners", "brand": "FastBreak", "desc": "Ultra-minimalist olive green trail-running shoes featuring a flexible zero-drop rubber sole molding perfectly to rough ground textures like a second skin.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBBarefootTrailRunnerOliveT2.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 2, "price": 85, "quality": "Rare", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0},
{"id": "shoes_fb_tennis_court_shoe_white_navy_t1", "name": "Navy/White Tennis Shoes", "brand": "FastBreak", "desc": "Crisp white performance tennis shoes engineered with excellent lateral stability, accented subtly by a sporty dark navy blue heel cup.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBTennisCourtShoeWhiteNavyT1.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 75, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_slip_on_athletic_knit_grey_t1", "name": "Grey Knit Walking Shoes", "brand": "FastBreak", "desc": "Incredibly comfortable grey heather slip-on knit athletic walkers. They stretch over the foot like a thick sock sitting on a bed of bouncy foam.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBSlipOnAthleticKnitGreyT1.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 2, "price": 50, "quality": "Common", "warmth": 2, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_water_shoe_teal_t0", "name": "Teal Water Shoes", "brand": "FastBreak", "desc": "Functional bright teal neoprene water socks designed with a sturdy rubber bottom for wading in rocky rivers or sandy beaches safely.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBWaterShoeTealT0.webp", "store": "storeSports", "slot": "shoes", "silhouette": "slipper", "baseLooks": 1, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_cushion_jogger_purple_t2", "name": "Pastel Jogging Sneakers", "brand": "FastBreak", "desc": "Ultra-plush jogging sneakers built with an oversized heel cushion and a breathable soft pastel purple mesh upper.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBCushionJoggerPurpleT2.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 85, "quality": "Rare", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_boxing_high_top_black_gold_t2", "name": "High-Top Boxing Trainers", "brand": "FastBreak", "desc": "Aggressive, extremely tall laced boxing shoes in matte black canvas, featuring a completely flat sole and striking gold trim.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBBoxingHighTopBlackGoldT2.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 4, "price": 100, "quality": "Rare", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 70, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0},
{"id": "shoes_fb_lifting_velcro_white_black_t2", "name": "Weightlifting Shoes", "brand": "FastBreak", "desc": "Stiff, rock-solid powerlifting shoes with a raised wooden heel and an unyielding mid-foot velcro strap locking the foot down tight.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBLiftingVelcroWhiteBlackT2.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 120, "quality": "Rare", "warmth": 2, "sexinessScore": 0, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_athletic_slide_orange_black_t1", "name": "Orange Strap Sport Slides", "brand": "FastBreak", "desc": "Lightweight, easy slip-on athletic slide sandals featuring a vivid orange synthetic top strap lined with soft padded jersey inside.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBAthleticSlideOrangeBlackT1.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sandal", "baseLooks": 2, "price": 35, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0},
{"id": "shoes_fb_minimalist_gym_barefoot_black_t2", "name": "Black Minimalist Gym Shoes", "brand": "FastBreak", "desc": "An aggressively stripped-down training shoe. Nearly paper-thin rubber sole paired with a stark black knit upper. You feel everything.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBMinimalistGymBarefootBlackT2.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 2, "price": 75, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_retro_90s_runner_silver_blue_t3", "name": "90s Retro Runners", "brand": "FastBreak", "desc": "Intensely complex retro running shoes overflowing with jagged metallic silver panels overlapping over deep blue mesh. A total 90s throwback.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBRetro90sRunnerSilverBlueT3.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 4, "price": 90, "quality": "Rare", "warmth": 2, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0},
{"id": "shoes_fb_cleated_turf_neon_yellow_t3", "name": "Neon Turf Cleats", "brand": "FastBreak", "desc": "Soccer/football turf cleats in blinding neon yellow. The rubber-studded sole guarantees impossible grip on artificial grass.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBCleatedTurfNeonYellowT3.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 105, "quality": "Premium", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "shoes_fb_winter_sport_fur_boot_black_t2", "name": "Black Fur Winter Boots", "brand": "FastBreak", "desc": "Heavy-duty waterproof athletic snow boots, completely lined inside and pouring out the top collar with luxuriously thick black faux fur.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBWinterSportFurBootBlackT2.webp", "store": "storeSports", "slot": "shoes", "silhouette": "boot", "baseLooks": 4, "price": 115, "quality": "Premium", "warmth": 5, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0},
{"id": "shoes_fb_skate_canvas_black_white_t1", "name": "Classic Skate Shoes", "brand": "FastBreak", "desc": "Flat-soled iconic black canvas skate shoes overlaid with tough white leather panelling forming a sleek side-stripe. Essential casual wear.", "image": "assets/content/clothing/shoes/FastBreak/shoesFBSkateCanvasBlackWhiteT1.webp", "store": "storeSports", "slot": "shoes", "silhouette": "sneaker", "baseLooks": 3, "price": 55, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 65, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sporty", "casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": true, "reqConfidence": 0, "reqExhibitionism": 0}
]>>/* Sleepwear DB reset */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.sleepwear = [
{ id: "pajama_color_owned", name: "Gray and Plaid Long Pajama Set", brand: "", desc: "Cozy long-sleeve dark gray top and long red-white-dark plaid pants, soft cotton, two-piece pajama set.", slot: "sleepwear", silhouette: "long sleeve plaid pajama set", baseLooks: 1, price: 0, quality: "Common", warmth: 1, sexinessScore: 0, exposureLevel: 0, durability: 100, tags: ["casual", "cozy", "basic"], npcAppeal: {}, shopAvailable: false, startOwned: true, reqConfidence: 0, image: "assets/content/clothing/sleepwear/Starter/pajama_color_owned.webp" },
{"id": "sleep_nl_basic_cotton_set_pink_t1", "name": "Pink Cotton Pajama Set", "brand": "Northline", "desc": "A very basic, comfortable matching two-piece pajama set consisting of a simple pink cotton t-shirt and loose matching shorts.", "image": "assets/content/clothing/sleepwear/Northline/sleepNLBasicCottonSetPinkT1.webp", "store": "storeClothingA", "slot": "sleepwear", "silhouette": "pajama-set", "baseLooks": 2, "price": 14, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 2, "durability": 40, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "warm", "modest"], "npcAppeal": {}, "shopAvailable": true, "startOwned": true},
{"id": "sleep_nl_fleece_pants_grey_t2", "name": "Grey Fleece Pajama Pants", "brand": "Northline", "desc": "Thick, warm, and incredibly cozy grey fleece sweatpants designed exclusively for sleeping and lounging during cold winter nights.", "image": "assets/content/clothing/sleepwear/Northline/sleepNLFleecePantsGreyT2.webp", "store": "storeClothingA", "slot": "sleepwear", "silhouette": "pajama-pants", "baseLooks": 1, "price": 18, "quality": "Common", "warmth": 5, "sexinessScore": 0, "exposureLevel": 0, "durability": 50, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "warm", "modest"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_nl_oversize_tee_white_t1", "name": "Oversized Sleep Shirt", "brand": "Northline", "desc": "The quintessential lazy sleepwear option. A giant, shapeless white t-shirt stolen straight from the men's department, worn completely bare underneath.", "image": "assets/content/clothing/sleepwear/Northline/sleepNLOversizeTeeWhiteT1.webp", "store": "storeClothingA", "slot": "sleepwear", "silhouette": "sleep-shirt", "baseLooks": 3, "price": 10, "quality": "Common", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 20, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "arousing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_nl_flannel_plaid_red_t2", "name": "Red Plaid Flannel PJ Set", "brand": "Northline", "desc": "A classically cozy matching long-sleeve, long-pant button-up sleepwear set presented in a warm red and black buffalo plaid flannel fabric.", "image": "assets/content/clothing/sleepwear/Northline/sleepNLFlannelPlaidRedT2.webp", "store": "storeClothingA", "slot": "sleepwear", "silhouette": "flannel-set", "baseLooks": 2, "price": 28, "quality": "Common", "warmth": 4, "sexinessScore": 0, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "warm", "modest"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_nl_jersey_nightgown_navy_t2", "name": "Navy Jersey Nightgown", "brand": "Northline", "desc": "A soft, modest knee-length nightgown created from breathable dark navy jersey cotton. Ideal for a good, unassuming night's sleep.", "image": "assets/content/clothing/sleepwear/Northline/sleepNLJerseyNightgownNavyT2.webp", "store": "storeClothingA", "slot": "sleepwear", "silhouette": "nightgown", "baseLooks": 2, "price": 18, "quality": "Common", "warmth": 2, "sexinessScore": 0, "exposureLevel": 2, "durability": 45, "reqConfidence": 5, "reqExhibitionism": 0, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "modest", "warm"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_nl_thermal_henley_grey_t2", "name": "Grey Thermal Sleep Henley", "brand": "Northline", "desc": "A heavy-duty snug thermal waffle-knit long sleeve henley top designed specifically for locking in body heat. Two small buttons at the neck.", "image": "assets/content/clothing/sleepwear/Northline/sleepNLThermalHenleyGreyT2.webp", "store": "storeClothingA", "slot": "sleepwear", "silhouette": "henley", "baseLooks": 2, "price": 20, "quality": "Common", "warmth": 3, "sexinessScore": 1, "exposureLevel": 1, "durability": 55, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "warm"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_nl_sport_lounge_set_black_t2", "name": "Black Sport Lounge Set", "brand": "Northline", "desc": "Athletic-inspired lounge wear. A tight black cotton bralette top paired seamlessly with high-waisted black ribbed lounge leggings.", "image": "assets/content/clothing/sleepwear/Northline/sleepNLSportLoungeSetBlackT2.webp", "store": "storeClothingA", "slot": "sleepwear", "silhouette": "lounge-set", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 50, "reqCorruption": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_satin_cami_shorts_pink_t3", "name": "Pink Satin Cami Set", "brand": "VERA Mode", "desc": "A trendy, ultra-soft powder pink satin camisole and matching ruffled sleep shorts. Lightweight and incredibly comfortable for lounging in style.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMSatinCamiShortsPinkT3.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "short-set", "baseLooks": 4, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 35, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_knit_wrap_robe_cream_t2", "name": "Cream Ribbed Knit Robe", "brand": "VERA Mode", "desc": "An effortless off-white ribbed knit wrap robe reaching just past the knees. Can be thrown over anything for instantaneous comfort and modest coverage.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMKnitWrapRobeCreamT2.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "robe", "baseLooks": 3, "price": 45, "quality": "Rare", "warmth": 3, "sexinessScore": 1, "exposureLevel": 1, "durability": 50, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "warm", "modest"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_slip_dress_floral_navy_t3", "name": "Navy Floral Slip Chemise", "brand": "VERA Mode", "desc": "A slightly romantic thigh-grazing slip dress cut on the bias from flowy, slinky navy material featuring delicate painted white flower prints.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMSlipDressFloralNavyT3.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "chemise", "baseLooks": 4, "price": 38, "quality": "Rare", "warmth": 1, "sexinessScore": 4, "exposureLevel": 4, "durability": 30, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_lounge_pants_wide_leg_ribbed_olive_t2", "name": "Olive Wide-Leg Sleep Pants", "brand": "VERA Mode", "desc": "Flowing beautifully loose around the ankles. These ultra-stretchy ribbed olive green pants define casual-chic lounging.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMLoungePantsWideLegRibbedOliveT2.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "pajama-pants", "baseLooks": 3, "price": 32, "quality": "Common", "warmth": 2, "sexinessScore": 1, "exposureLevel": 1, "durability": 45, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "modest"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_tee_boy_shorts_grey_stripe_t2", "name": "Striped Lounge Boy Shorts", "brand": "VERA Mode", "desc": "A trendy matched sleepwear two-piece. Features heather grey and white horizontal stripes across a cozy tight crop-top and cheeky boy shorts.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMTeeBoyShortsGreyStripeT2.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "short-set", "baseLooks": 3, "price": 28, "quality": "Common", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 40, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_satin_button_down_black_piping_t3", "name": "Satin PJ with Piping", "brand": "VERA Mode", "desc": "The ultimate classic cinematic pajama suite in heavy midnight black satin. Lined flawlessly around the collar, sleeve, and wide pants with stark white piping.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMSatinButtonDownBlackPipingT3.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "pajama-set", "baseLooks": 5, "price": 55, "quality": "Rare", "warmth": 3, "sexinessScore": 2, "exposureLevel": 1, "durability": 50, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "modest"], "npcAppeal": {"elite": 2, "professional": 2}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_slip_dress_leopard_t4", "name": "Leopard Print Silk Chemise", "brand": "VERA Mode", "desc": "A trendy, effortlessly sexy bias-cut short slip chemise in glossy leopard-printed satin. A staple piece for confident lounging.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMSlipDressLeopardT4.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "chemise", "baseLooks": 6, "price": 45, "quality": "Rare", "warmth": 1, "sexinessScore": 6, "exposureLevel": 4, "durability": 30, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "chic", "sexy"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_velour_sweatsuit_plum_t3", "name": "Plum Velour Sweatsuit", "brand": "VERA Mode", "desc": "An aggressively cozy matching zip-up velour hoodie and track pants set in dark plum purple. Giving 2000s off-duty celebrity vibes.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMVelourSweatsuitPlumT3.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "loungewear", "baseLooks": 4, "price": 55, "quality": "Rare", "warmth": 4, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty", "warm"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_lace_trim_ribbed_tank_olive_t2", "name": "Lace-Trim Olive Tank Set", "brand": "VERA Mode", "desc": "A delicate yet casual ribbed-knit olive cotton tank top with black eyelash lace spilling over the neckline. Paired with coordinating ribbed boyshorts.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMLaceTrimRibbedTankOliveT2.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "short-set", "baseLooks": 3, "price": 28, "quality": "Common", "warmth": 1, "sexinessScore": 3, "exposureLevel": 3, "durability": 40, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_boyfriend_cardigan_oatmeal_t2", "name": "Oatmeal Knit Cardigan Set", "brand": "VERA Mode", "desc": "Lounge perfection. This outfit includes a huge, deeply chunky oatmeal-colored oversized boyfriend cardigan worn carelessly over a tiny matching knit bra and short-shorts.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMBoyfriendCardiganOatmealT2.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "loungewear", "baseLooks": 4, "price": 50, "quality": "Rare", "warmth": 3, "sexinessScore": 4, "exposureLevel": 4, "durability": 45, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "warm", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_vm_satin_kimono_robe_blush_t3", "name": "Blush Satin Kimono Robe", "brand": "VERA Mode", "desc": "A breathtakingly sheer, lightweight pastel blush satin robe boasting authentic wide kimono sleeves. Meant for sipping coffee on a balcony.", "image": "assets/content/clothing/sleepwear/VeraMode/sleepVMSatinKimonoRobeBlushT3.webp", "store": "storeClothingB", "slot": "sleepwear", "silhouette": "robe", "baseLooks": 6, "price": 60, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 3, "durability": 30, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "cute", "chic"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_fc_silk_long_pajama_navy_white_t3", "name": "Navy Silk Long Pajama Set", "brand": "Fifth Avenue", "desc": "A timeless 100% pure silk full-length pajama suit. Tailored sharply in midnight navy with elegant stark white piping edges. The pinnacle of sophisticated lounging.", "image": "assets/content/clothing/sleepwear/FifthAvenue/sleepFCSilkLongPajamaNavyWhiteT3.webp", "store": "storeClothingC", "slot": "sleepwear", "silhouette": "pajama-set", "baseLooks": 6, "price": 120, "quality": "Premium", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 30, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "modest", "professional"], "npcAppeal": {"elite": 4, "professional": 4}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_fc_mulberry_silk_slip_champagne_t4", "name": "Mulberry Silk Slip Dress", "brand": "Fifth Avenue", "desc": "An impeccably simple, fluid chemise slip crafted from the highest grade, heavy champagne-colored mulberry silk. Fits like a second, glowing skin.", "image": "assets/content/clothing/sleepwear/FifthAvenue/sleepFCMulberrySilkSlipChampagneT4.webp", "store": "storeClothingC", "slot": "sleepwear", "silhouette": "chemise", "baseLooks": 6, "price": 95, "quality": "Premium", "warmth": 1, "sexinessScore": 5, "exposureLevel": 4, "durability": 25, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "casual"], "npcAppeal": {"elite": 4}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_fc_cashmere_lounge_set_grey_t4", "name": "Grey Cashmere Lounge Set", "brand": "Fifth Avenue", "desc": "Unrivalled winter luxury. A thick yet light-as-air 100% mongolian cashmere sweater and matching wide-leg trousers in soft dove grey.", "image": "assets/content/clothing/sleepwear/FifthAvenue/sleepFCCashmereLoungeSetGreyT4.webp", "store": "storeClothingC", "slot": "sleepwear", "silhouette": "lounge-set", "baseLooks": 6, "price": 280, "quality": "Premium", "warmth": 4, "sexinessScore": 1, "exposureLevel": 1, "durability": 30, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "modest", "warm"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_fc_silk_wrap_robe_black_t4", "name": "Black Silk Maxi Robe", "brand": "Fifth Avenue", "desc": "A sweeping, dramatic floor-length pure silk wrap robe in intense obsidian black. Fastens at the waist, billowing out behind her as she walks.", "image": "assets/content/clothing/sleepwear/FifthAvenue/sleepFCSilkWrapRobeBlackT4.webp", "store": "storeClothingC", "slot": "sleepwear", "silhouette": "robe", "baseLooks": 7, "price": 145, "quality": "Luxury", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 30, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "glamorous", "modest"], "npcAppeal": {"elite": 4}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_sl_babydoll_mesh_pink_t3", "name": "Pink Mesh Babydoll Set", "brand": "Silk & Lace", "desc": "A romantically sweet empire-waist babydoll dress in sheer pastel pink mesh, complete with underwire demi-cups and a matching tiny G-string.", "image": "assets/content/clothing/sleepwear/SilkLace/sleepSLBabydollMeshPinkT3.webp", "store": "storeLingerieA", "slot": "sleepwear", "silhouette": "babydoll", "baseLooks": 6, "price": 60, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 30, "reqCorruption": 4, "matchSet": null, "tags": ["cute", "sexy", "revealing"], "npcAppeal": {"rich": 3}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_sl_chemise_lace_trim_navy_t3", "name": "Navy Lace-Trim Chemise", "brand": "Silk & Lace", "desc": "Fluid dark navy blue satin bias-cut slip dress, delicately edged with scalloped black eyelash lace around the neckline and sweeping hem.", "image": "assets/content/clothing/sleepwear/SilkLace/sleepSLChemiseLaceTrimNavyT3.webp", "store": "storeLingerieA", "slot": "sleepwear", "silhouette": "chemise", "baseLooks": 6, "price": 75, "quality": "Premium", "warmth": 1, "sexinessScore": 5, "exposureLevel": 4, "durability": 35, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_sl_robe_sheer_lace_white_t4", "name": "Sheer White Lace Robe", "brand": "Silk & Lace", "desc": "An absolutely stunning lightweight floor-sweeping bridal robe crafted entirely from transparent white floral tulle lace. Fastens with a single satin ribbon.", "image": "assets/content/clothing/sleepwear/SilkLace/sleepSLRobeSheerLaceWhiteT4.webp", "store": "storeLingerieA", "slot": "sleepwear", "silhouette": "robe", "baseLooks": 7, "price": 145, "quality": "Premium", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 20, "reqCorruption": 4, "matchSet": null, "tags": ["elegant", "sexy", "revealing", "formal"], "npcAppeal": {"elite": 4}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_sl_teddy_silk_lace_red_t3", "name": "Crimson Silk & Lace Teddy", "brand": "Silk & Lace", "desc": "A sultry one-piece teddy sleepsuit. The body is formed from slinky ruby red silk, transitioning diagonally into sheer black lace panels at the hips and chest.", "image": "assets/content/clothing/sleepwear/SilkLace/sleepSLTeddySilkLaceRedT3.webp", "store": "storeLingerieA", "slot": "sleepwear", "silhouette": "teddy", "baseLooks": 6, "price": 85, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 5, "durability": 30, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_sl_satin_pj_short_set_emerald_t3", "name": "Emerald Fluted Satin Pajamas", "brand": "Silk & Lace", "desc": "Opulent luxury sleepwear. A rich emerald green silk button-up top and fluted matching shorts. Perfect for lounging with a martini.", "image": "assets/content/clothing/sleepwear/SilkLace/sleepSLSatinPJShortSetEmeraldT3.webp", "store": "storeLingerieA", "slot": "sleepwear", "silhouette": "pajama-set", "baseLooks": 5, "price": 90, "quality": "Premium", "warmth": 1, "sexinessScore": 4, "exposureLevel": 2, "durability": 45, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "modest"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_sl_playsuit_satin_champagne_t3", "name": "Champagne Silk Playsuit", "brand": "Silk & Lace", "desc": "A charming, single-piece sleep romper (playsuit) cut in fluid champagne satin. Features thin straps, a soft draped cowl neck, and flowy short legs.", "image": "assets/content/clothing/sleepwear/SilkLace/sleepSLPlaysuitSatinChampagneT3.webp", "store": "storeLingerieA", "slot": "sleepwear", "silhouette": "romper", "baseLooks": 5, "price": 85, "quality": "Premium", "warmth": 1, "sexinessScore": 4, "exposureLevel": 3, "durability": 40, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "elegant", "casual"], "npcAppeal": {"elite": 2}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_is_robe_fishnet_black_t4", "name": "Black Fishnet Robe", "brand": "Intimate Secrets", "desc": "A fully open, intensely see-through sheer black mesh fishnet long line cover-up robe serving absolutely zero functional warmth.", "image": "assets/content/clothing/sleepwear/IntimateSecrets/sleepISRobeFishnetBlackT4.webp", "store": "storeLingerieB", "slot": "sleepwear", "silhouette": "robe", "baseLooks": 6, "price": 50, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 8, "durability": 20, "reqCorruption": 6, "matchSet": null, "tags": ["revealing", "arousing", "sexy", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_is_chemise_latex_zip_black_t5", "name": "Latex Zip-Front Chemise", "brand": "Intimate Secrets", "desc": "The ultimate fetishwear sleep outfit. A skin-tight black oiled latex mini chemise with a heavy silver zipper running dangerously down the full front.", "image": "assets/content/clothing/sleepwear/IntimateSecrets/sleepISChemiseLatexZipBlackT5.webp", "store": "storeLingerieB", "slot": "sleepwear", "silhouette": "chemise", "baseLooks": 8, "price": 125, "quality": "Luxury", "warmth": 0, "sexinessScore": 9, "exposureLevel": 6, "durability": 50, "reqCorruption": 8, "matchSet": null, "tags": ["arousing", "daring", "lewd", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_is_babydoll_sheer_lace_red_t5", "name": "Crimson Lace Babydoll", "brand": "Intimate Secrets", "desc": "A sexy crimson red babydoll in sheer mesh with transparent demi-cups and delicate lace trim. Flowy tulle skirt falls from under the bust to mid-thigh.", "image": "assets/content/clothing/sleepwear/IntimateSecrets/sleepISBabydollSheerLaceRedT5.webp", "store": "storeLingerieB", "slot": "sleepwear", "silhouette": "babydoll", "baseLooks": 7, "price": 85, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 6, "durability": 30, "reqCorruption": 6, "matchSet": null, "tags": ["sexy", "daring", "arousing", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_is_lace_crop_shorts_crotchless_black_t5", "name": "Crotchless Lace Sleep Set", "brand": "Intimate Secrets", "desc": "Two incredibly soft, unstructured pieces of sheer black eyelash lace masquerading as sleepwear. Features a cropped bralette and fully crotchless lace sleep shorts.", "image": "assets/content/clothing/sleepwear/IntimateSecrets/sleepISLaceCropShortsCrotchlessBlackT5.webp", "store": "storeLingerieB", "slot": "sleepwear", "silhouette": "pajama-set", "baseLooks": 7, "price": 105, "quality": "Premium", "warmth": 0, "sexinessScore": 10, "exposureLevel": 10, "durability": 20, "reqCorruption": 8, "matchSet": null, "tags": ["erotic", "slutty", "arousing", "crotchless"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false},
{"id": "sleep_is_micro_robe_silk_pink_t4", "name": "Micro Silk Pink Robe", "brand": "Intimate Secrets", "desc": "A shiny, highly seductive hot pink silk robe intentionally cut scandalously short. Barely drops past the hips, exposing vast amounts of leg and thigh.", "image": "assets/content/clothing/sleepwear/IntimateSecrets/sleepISMicroRobeSilkPinkT4.webp", "store": "storeLingerieB", "slot": "sleepwear", "silhouette": "robe", "baseLooks": 5, "price": 75, "quality": "Premium", "warmth": 1, "sexinessScore": 7, "exposureLevel": 7, "durability": 40, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "daring", "arousing", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false}
]>>/* Socks DB reset */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.socks = [
{ id: "socks_white_owned", name: "White Everyday Socks", brand: "", desc: "Simple white ankle socks.", slot: "socks", silhouette: "ankle socks", baseLooks: 1, price: 0, quality: "Common", warmth: 0, sexinessScore: 0, exposureLevel: 0, durability: 100, tags: ["casual", "basic", "mild"], npcAppeal: {}, shopAvailable: false, startOwned: true, reqConfidence: 0, image: "assets/content/clothing/socks/Starter/socks_white_owned.webp" },
{ id: "socks_black_owned", name: "Black Everyday Socks", brand: "", desc: "Simple black ankle socks.", slot: "socks", silhouette: "ankle socks", baseLooks: 1, price: 0, quality: "Common", warmth: 0, sexinessScore: 0, exposureLevel: 0, durability: 100, tags: ["casual", "basic", "mild"], npcAppeal: {}, shopAvailable: false, startOwned: true, reqConfidence: 0, image: "assets/content/clothing/socks/Starter/socks_black_owned.webp" },
{"id": "socks_north_ankle_white", "name": "White Cotton Ankle Socks", "brand": "Northline Apparel", "desc": "Basic white cotton-blend ankle socks with a ribbed cuff. A casual everyday essential.", "image": "assets/content/clothing/socks/NorthlineApparel/socksNorthAnkleWhite.webp", "store": "storeClothingA", "slot": "socks", "silhouette": "ankle-socks", "baseLooks": 1, "price": 5, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0},
{"id": "socks_north_ankle_black", "name": "Black Cotton Ankle Socks", "brand": "Northline Apparel", "desc": "Basic black cotton-blend ankle socks offering a neat, minimal look with any casual shoe.", "image": "assets/content/clothing/socks/NorthlineApparel/socksNorthAnkleBlack.webp", "store": "storeClothingA", "slot": "socks", "silhouette": "ankle-socks", "baseLooks": 1, "price": 5, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 100, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "socks_north_crew_sport_white", "name": "White Striped Crew Socks", "brand": "Northline Apparel", "desc": "White sporting crew socks hitting mid-calf, featuring two classic navy stripes at the top.", "image": "assets/content/clothing/socks/NorthlineApparel/socksNorthCrewSportWhite.webp", "store": "storeClothingA", "slot": "socks", "silhouette": "crew-socks", "baseLooks": 2, "price": 5, "quality": "Common", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "sporty", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "socks_north_knee_high_black", "name": "Black Opaque Knee-High Socks", "brand": "Northline Apparel", "desc": "Solid black opaque knee-high socks offering warmth and a preppy or alternative casual look.", "image": "assets/content/clothing/socks/NorthlineApparel/socksNorthKneeHighBlack.webp", "store": "storeClothingA", "slot": "socks", "silhouette": "knee-highs", "baseLooks": 3, "price": 10, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "socks_north_thigh_high_ribbed_grey", "name": "Grey Ribbed Thigh-High Socks", "brand": "Northline Apparel", "desc": "Cosy heather grey ribbed knit thigh-high socks. Perfect for lounging comfortably at home.", "image": "assets/content/clothing/socks/NorthlineApparel/socksNorthThighHighRibbedGrey.webp", "store": "storeClothingA", "slot": "socks", "silhouette": "thigh-highs", "baseLooks": 3, "price": 10, "quality": "Rare", "warmth": 3, "sexinessScore": 3, "exposureLevel": 3, "durability": 100, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "socks_north_ankle_frill_pink", "name": "Soft Pink Frill Ankle Socks", "brand": "Northline Apparel", "desc": "Soft pastel pink ankle socks finished with a cute ruffled frill at the top edge.", "image": "assets/content/clothing/socks/NorthlineApparel/socksNorthAnkleFrillPink.webp", "store": "storeClothingA", "slot": "socks", "silhouette": "ankle-socks", "baseLooks": 3, "price": 5, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 2, "durability": 100, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "socks_sl_fishnet_ankle_black", "name": "Black Fishnet Ankle Socks", "brand": "Silk & Lace", "desc": "Short black fishnet ankle socks with a solid elastic cuff, perfect for peeking out of heels or boots.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLFishnetAnkleBlack.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "ankle-socks", "baseLooks": 4, "price": 10, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 60, "reqCorruption": 1, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "socks_sl_sheer_ankle_nude", "name": "Nude Sheer Ankle Socks", "brand": "Silk & Lace", "desc": "Ultra-sheer nude ankle socks offering an invisible protective layer with a subtle, polished sheen.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLSheerAnkleNude.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "ankle-socks", "baseLooks": 2, "price": 10, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 0, "durability": 60, "reqCorruption": 1, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0},
{"id": "socks_sl_sheer_knee_high_black", "name": "Black Sheer Knee-Highs", "brand": "Silk & Lace", "desc": "Classic black sheer knee-high stockings with a reinforced toe and comfortable wide top band.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLSheerKneeHighBlack.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "knee-highs", "baseLooks": 3, "price": 10, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 1, "durability": 60, "reqCorruption": 1, "reqHeelsSkill": 0, "matchSet": null, "tags": ["formal", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0},
{"id": "socks_sl_lace_top_knee_high_white", "name": "White Lace-Top Knee-Highs", "brand": "Silk & Lace", "desc": "Soft white opaque knee-high stockings finished with an elegant wide lace trim at the top.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLLaceTopKneeHighWhite.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "knee-highs", "baseLooks": 4, "price": 15, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 3, "durability": 60, "reqCorruption": 1, "reqHeelsSkill": 0, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0},
{"id": "socks_sl_stay_up_sheer_nude", "name": "Nude Sheer Stay-Up Thigh-Highs", "brand": "Silk & Lace", "desc": "Bare-look sheer nude thigh-high stockings with a silicone-lined plain band designed to stay up without a garter.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLStayUpSheerNude.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "thigh-highs", "baseLooks": 4, "price": 15, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 2, "durability": 60, "reqCorruption": 1, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0},
{"id": "socks_sl_stay_up_lace_black", "name": "Black Lace-Top Stay-Ups", "brand": "Silk & Lace", "desc": "Classic black sheer thigh-high stockings topped with a deep floral lace silicone-backed stay-up band.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLStayUpLaceBlack.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "thigh-highs", "baseLooks": 5, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "socks_sl_stocking_seamed_black", "name": "Black Back-Seam Stockings", "brand": "Silk & Lace", "desc": "Vintage-inspired black sheer thigh-high stockings featuring a bold black line running up the back and a reinforced heel. Requires a garter.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLStockingSeamedBlack.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "stockings", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 60, "reqCorruption": 3, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sexy", "elegant", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0},
{"id": "socks_sl_stocking_plain_nude", "name": "Nude Sheer Stockings", "brand": "Silk & Lace", "desc": "Ultra-sheer bare-look nude stockings with a plain flat welt at the thigh. Designed to be attached to a suspender belt.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLStockingPlainNude.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "stockings", "baseLooks": 4, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0},
{"id": "socks_sl_stocking_fishnet_red", "name": "Red Fishnet Stockings", "brand": "Silk & Lace", "desc": "Vibrant red wide-diamond fishnet stockings with a solid ribbed thigh welt. Requires a garter belt.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLStockingFishnetRed.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "stockings", "baseLooks": 5, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 60, "reqCorruption": 3, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sexy", "daring", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0},
{"id": "socks_sl_pantyhose_sheer_black", "name": "Black Sheer Tights", "brand": "Silk & Lace", "desc": "Classic 15-denier black sheer tights (pantyhose) offering full leg coverage from toe to waist.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLPantyhoseSheerBlack.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "pantyhose", "baseLooks": 4, "price": 15, "quality": "Common", "warmth": 1, "sexinessScore": 3, "exposureLevel": 1, "durability": 60, "reqCorruption": 1, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "formal", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0},
{"id": "socks_sl_pantyhose_opaque_black", "name": "Black Opaque Tights", "brand": "Silk & Lace", "desc": "Thick 80-denier solid black opaque tights designed for colder days and a completely blacked-out leg appearance.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLPantyhoseOpaqueBlack.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "pantyhose", "baseLooks": 3, "price": 20, "quality": "Common", "warmth": 3, "sexinessScore": 2, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "reqHeelsSkill": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0},
{"id": "socks_sl_pantyhose_fishnet_nude", "name": "Nude Micro-Fishnet Tights", "brand": "Silk & Lace", "desc": "Delicate nude-colored micro-fishnet tights that blend with the skin to create a subtly textured, flawless finish from toe to waist.", "image": "assets/content/clothing/socks/SilkAndLace/socksSLPantyhoseFishnetNude.webp", "store": "storeLingerieA", "slot": "socks", "silhouette": "pantyhose", "baseLooks": 5, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "socks_is_stocking_lace_top_red_t3", "name": "Red Lace-Top Sheer Stockings", "brand": "Intimate Secrets", "desc": "Bold red sheer thigh-high stockings featuring a thick floral lace band at the top. Designed to attach to garters.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISStockingLaceTopRedT3.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "stockings", "baseLooks": 5, "price": 25, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "reqHeelsSkill": 0, "matchSet": "intimateRedLaceSet", "tags": ["sexy", "daring", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "socks_is_stocking_seamed_red_t3", "name": "Red Back-Seam Stockings", "brand": "Intimate Secrets", "desc": "Sheer red stockings styled with a provocative contrasting black back seam running straight down to a black reinforced heel.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISStockingSeamedRedT3.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "stockings", "baseLooks": 5, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 3, "durability": 60, "reqCorruption": 2, "reqHeelsSkill": 0, "matchSet": null, "tags": ["sexy", "daring", "revealing", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "socks_is_pantyhose_corset_lacing_t4", "name": "Corset-Laced Back Tights", "brand": "Intimate Secrets", "desc": "Provocative sheer black tights featuring a faux corset lacing detail that runs dramatically down the back of the calves.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISPantyhoseCorsetLacingT4.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "pantyhose", "baseLooks": 6, "price": 35, "quality": "Rare", "warmth": 1, "sexinessScore": 6, "exposureLevel": 2, "durability": 60, "reqCorruption": 3, "reqHeelsSkill": 0, "matchSet": null, "tags": ["daring", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0},
{"id": "socks_is_stay_up_fishnet_bow_t4", "name": "Fishnet Bow-Back Stay-Ups", "brand": "Intimate Secrets", "desc": "Wide-diamond black fishnet stay-up stockings with a solid silicone top band, decorated with a large red satin bow on the lower back thigh.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISStayUpFishnetBowT4.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "thigh-highs", "baseLooks": 6, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 3, "durability": 60, "reqCorruption": 4, "reqHeelsSkill": 0, "matchSet": null, "tags": ["daring", "lewd", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0},
{"id": "socks_is_stocking_strappy_garter_t5", "name": "Built-In Strappy Stockings", "brand": "Intimate Secrets", "desc": "Aggressive all-in-one thigh-high stockings featuring built-in crisscrossing elastic straps over the thighs instead of a traditional plain band.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISStockingStrappyGarterT5.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "stockings", "baseLooks": 6, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 3, "durability": 60, "reqCorruption": 4, "reqHeelsSkill": 0, "matchSet": "intimateBlackMeshSet", "tags": ["daring", "erotic", "lewd"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0},
{"id": "socks_is_pantyhose_wet_look_black_t5", "name": "Wet-Look Faux Leather Tights", "brand": "Intimate Secrets", "desc": "Glossy sheer-to-waist wet-look faux leather tights that hug every curve, making the legs look dangerously sleek and polished.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISPantyhoseWetLookBlackT5.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "pantyhose", "baseLooks": 6, "price": 45, "quality": "Premium", "warmth": 2, "sexinessScore": 6, "exposureLevel": 2, "durability": 80, "reqCorruption": 5, "reqHeelsSkill": 0, "matchSet": null, "tags": ["daring", "lewd", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 30},
{"id": "socks_is_pantyhose_floral_lace_t5", "name": "Full Floral Lace Tights", "brand": "Intimate Secrets", "desc": "Sheer-to-waist pantyhose crafted entirely from complex black floral lace, creating an intensely romantic yet exhibitionist effect.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISPantyhoseFloralLaceT5.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "pantyhose", "baseLooks": 7, "price": 45, "quality": "Premium", "warmth": 1, "sexinessScore": 7, "exposureLevel": 2, "durability": 60, "reqCorruption": 3, "reqHeelsSkill": 0, "matchSet": null, "tags": ["daring", "erotic"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0},
{"id": "socks_is_pantyhose_crotchless_fishnet_t5", "name": "Fishnet Crotchless Tights", "brand": "Intimate Secrets", "desc": "Classic black fishnet tights modernized with an entirely open crotch. The ultimate erotic staple.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISPantyhoseCrotchlessFishnetT5.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "pantyhose", "baseLooks": 6, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 5, "durability": 60, "reqCorruption": 5, "reqHeelsSkill": 0, "matchSet": null, "tags": ["daring", "erotic", "lewd", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 20},
{"id": "socks_is_micromesh_suspender_tights_t5", "name": "Micromesh Suspender Tights", "brand": "Intimate Secrets", "desc": "A wicked hybrid piece bridging tights and stockings; sheer black thigh-high segments that seamlessly connect to a micromesh waist belt, leaving the upper thighs permanently exposed.", "image": "assets/content/clothing/socks/IntimateSecrets/socksISMicromeshSuspenderTightsT5.webp", "store": "storeLingerieB", "slot": "socks", "silhouette": "pantyhose", "baseLooks": 7, "price": 50, "quality": "Luxury", "warmth": 1, "sexinessScore": 7, "exposureLevel": 4, "durability": 60, "reqCorruption": 2, "reqHeelsSkill": 0, "matchSet": null, "tags": ["daring", "erotic", "lewd", "revealing"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0}
]>>/* special/apron DB */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.apron = [
{"id":"apronRubysDishwash","name":"Ruby's Diner Dishwasher Apron","brand":"","desc":"Work apron issued for dishwasher shifts at Ruby's Diner. Heavy fabric, ties at the back.","image":"assets/content/clothing/others/RubysDiner/apronRubysDishwash.webp","store":"","slot":"apron","silhouette":"apron","baseLooks":1,"price":0,"quality":"Common","warmth":1,"sexinessScore":0,"exposureLevel":0,"durability":100,"reqCorruption":0,"matchSet":null,"tags":["work","uniform"],"npcAppeal":{},"shopAvailable":false,"startOwned":false,"reqConfidence":0}
]>>/* swimsuits DB reset */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.swimsuits = [
{"id": "swimsuit_nl_classic_red_t1", "name": "Classic Red Pool Suit", "brand": "Northline", "desc": "An instantly recognizable classic one-piece swimsuit in bright solid red. Thick shoulder straps and standard coverage everywhere.", "image": "assets/content/clothing/swimsuit/Northline/swimsuitNLClassicRedT1.webp", "store": "storeClothingA", "slot": "swimsuit", "silhouette": "scoop-neck", "baseLooks": 2, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 3, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_nl_athletic_racerback_black_t2", "name": "Athletic Black Racerback", "brand": "Northline", "desc": "Built for swimming laps. A durable, tight-fitting black racerback swimsuit with subtle white side panels to streamline the silhouette.", "image": "assets/content/clothing/swimsuit/Northline/swimsuitNLAthleticRacerbackBlackT2.webp", "store": "storeClothingA", "slot": "swimsuit", "silhouette": "racerback", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 2, "durability": 70, "reqCorruption": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_al_racerback_sporty_navy_t1", "name": "Navy Sport Racerback", "brand": "Aqua Lane", "desc": "Classic athletic one-piece swimsuit in solid navy blue with white piped edges. Designed for lap swimming with a secure racerback fit.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALRacerbackSportyNavyT1.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "racerback", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 3, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_al_deep_plunge_tropical_t2", "name": "Deep-Plunge Tropical One-Piece", "brand": "Aqua Lane", "desc": "Bold tropical print one-piece featuring a deep plunging V-neckline that drops to the midriff. Halter tie neck for adjustable lift.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALDeepPlungeTropicalT2.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "deep-plunge", "baseLooks": 4, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_al_mesh_panel_black_neon_t2", "name": "Neon Mesh-Panel Swimsuit", "brand": "Aqua Lane", "desc": "Athletic black one-piece accented with sporty neon yellow side stripes and unpadded sheer mesh cutout panels at the waist to emphasize the wearer's curves.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALMeshPanelBlackNeonT2.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "mesh-panel", "baseLooks": 4, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["sporty", "daring", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "swimsuit_al_zip_scuba_color_block_t3", "name": "Color-Block Zip Scuba Suit", "brand": "Aqua Lane", "desc": "Thick neoprene high-performance scuba swimsuit. Features a striking teal, white, and yellow color-block design with a functional silver zipper running down the front.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALZipScubaColorBlockT3.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "scuba", "baseLooks": 5, "price": 75, "quality": "Premium", "warmth": 1, "sexinessScore": 5, "exposureLevel": 4, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["sporty", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_al_asymmetric_cut_out_red_t3", "name": "Asymmetric Red Cut-Out", "brand": "Aqua Lane", "desc": "Avant-garde asymmetric swimsuit in bright cherry red. A sharp diagonal slash leaves half the torso completely completely bare, balancing between a one-piece and a bikini.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALAsymmetricCutOutRedT3.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "asymmetric", "baseLooks": 6, "price": 85, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 60, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing", "daring"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_al_belted_ribbed_white_t2", "name": "White Ribbed Belted Suit", "brand": "Aqua Lane", "desc": "Chic ribbed textured swimsuit in crisp pure white. Features a flattering built-in waist belt with a faux tortoiseshell buckle.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALBeltedRibbedWhiteT2.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "belted", "baseLooks": 4, "price": 55, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 4, "durability": 60, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "casual", "cute"], "npcAppeal": {"rich": 3}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_al_lace_up_sides_leopard_t3", "name": "Leopard Lace-Up Suit", "brand": "Aqua Lane", "desc": "Fierce exotic leopard print one-piece. The entire side profiles are sliced open and held together by seductive corset-style lace-up strings.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALLaceUpSidesLeopardT3.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "lace-up", "baseLooks": 5, "price": 60, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 60, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_al_high_cut_80s_neon_teal_t3", "name": "80s High-Cut Neon Suit", "brand": "Aqua Lane", "desc": "Extreme high-cut retro one-piece in vibrant neon teal. Sits impossibly high on the hip bones to elongate the legs. Deep scooped back.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALHighCut80sNeonTealT3.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "high-cut", "baseLooks": 6, "price": 50, "quality": "Premium", "warmth": 0, "sexinessScore": 4, "exposureLevel": 6, "durability": 60, "reqCorruption": 0, "matchSet": null, "tags": ["sporty", "sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "swimsuit_al_illusion_sheer_mesh_black_t4", "name": "Sheer Illusion Black Suit", "brand": "Aqua Lane", "desc": "A jaw-dropping sheer illusion swimsuit. Solid matte black strategically covers your vitals, while the rest of the torso is wrapped in transparent micromesh.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALIllusionSheerMeshBlackT4.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "sheer-illusion", "baseLooks": 7, "price": 95, "quality": "Premium", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 60, "reqCorruption": 5, "matchSet": null, "tags": ["sexy", "revealing", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 85, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "swimsuit_al_glamour_chain_gold_backless_t4", "name": "Gold Chain Backless Suit", "brand": "Aqua Lane", "desc": "The pinnacle of beachside luxury. A slinky white plunging swimsuit where the entire back and shoulder structure is composed of thick gold plated chains.", "image": "assets/content/clothing/swimsuit/AquaLane/swimsuitALGlamourChainGoldBacklessT4.webp", "store": "storeSwim", "slot": "swimsuit", "silhouette": "halter-plunge", "baseLooks": 9, "price": 145, "quality": "Luxury", "warmth": 0, "sexinessScore": 9, "exposureLevel": 8, "durability": 60, "reqCorruption": 5, "matchSet": null, "tags": ["sexy", "elegant", "revealing"], "npcAppeal": {"elite": 5}, "shopAvailable": true, "startOwned": false, "reqConfidence": 100, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/* Cloting Tops Database */
<<set setup.clothingData = setup.clothingData || {}>>
<<set setup.clothingData.tops = [
{"id":"tshirt_white_owned","name":"White Basic T-Shirt","brand":"","desc":"Simple white cotton t-shirt for everyday wear.","image":"assets/content/clothing/tops/Starter/tshirt_white_owned.webp","store":"","slot":"top","silhouette":"basic tee","baseLooks":1,"price":0,"quality":"Common","warmth":0,"sexinessScore":0,"exposureLevel":0,"durability":100,"reqCorruption":0,"matchSet":null,"tags":["casual","basic","mild"],"npcAppeal":{},"shopAvailable":false,"startOwned":true,"reqConfidence":0},
{"id":"tshirt_blue_owned","name":"Blue Basic T-Shirt","brand":"","desc":"Simple blue cotton t-shirt for everyday wear.","image":"assets/content/clothing/tops/Starter/tshirt_blue_owned.webp","store":"","slot":"top","silhouette":"basic tee","baseLooks":1,"price":0,"quality":"Common","warmth":0,"sexinessScore":0,"exposureLevel":0,"durability":100,"reqCorruption":0,"matchSet":null,"tags":["casual","basic","mild"],"npcAppeal":{},"shopAvailable":false,"startOwned":true,"reqConfidence":0},
{"id":"tshirt_gray_owned","name":"Gray Basic T-Shirt","brand":"","desc":"Simple gray cotton t-shirt for everyday wear.","image":"assets/content/clothing/tops/Starter/tshirt_gray_owned.webp","store":"","slot":"top","silhouette":"basic tee","baseLooks":1,"price":0,"quality":"Common","warmth":0,"sexinessScore":0,"exposureLevel":0,"durability":100,"reqCorruption":0,"matchSet":null,"tags":["sporty","basic"],"npcAppeal":{},"shopAvailable":false,"startOwned":true,"reqConfidence":0},
{"id": "top_oversized_graphic_white", "name": "White Oversized Graphic Print Tee", "brand": "Northline Apparel", "desc": "A relaxed oversized white cotton jersey tee featuring a vintage-inspired graphic print on the front and dropped shoulders.", "image": "assets/content/clothing/tops/NorthlineApparel/topOversizedGraphicWhite.webp", "store": "storeClothingA", "slot": "top", "silhouette": "oversized", "baseLooks": 3, "price": 15, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_ribbed_henley_navy", "name": "Navy Blue Ribbed Long-Sleeve Henley", "brand": "Northline Apparel", "desc": "A fitted dark navy blue ribbed cotton blend long-sleeve henley top with a functional three-button placket at the neckline.", "image": "assets/content/clothing/tops/NorthlineApparel/topRibbedHenleyNavy.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 4, "price": 15, "quality": "Common", "warmth": 1, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_bretton_stripe_navy_white", "name": "Navy & White Breton Stripe Fitted Tee", "brand": "Northline Apparel", "desc": "A classic navy blue and soft white horizontal Breton stripe fitted cotton jersey tee with a round neckline, short sleeves, and clean even stripe pattern throughout.", "image": "assets/content/clothing/tops/NorthlineApparel/topBrettonStripeNavyWhite.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 3, "price": 15, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_plaid_flannel_red_black", "name": "Red & Black Plaid Flannel Shirt", "brand": "Northline Apparel", "desc": "A cozy deep red and black plaid pattern flannel shirt with a button-up front, worn slightly oversized for a relaxed fit.", "image": "assets/content/clothing/tops/NorthlineApparel/topPlaidFlannelRedBlack.webp", "store": "storeClothingA", "slot": "top", "silhouette": "oversized", "baseLooks": 4, "price": 25, "quality": "Common", "warmth": 3, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_waffle_knit_olive", "name": "Olive Green Waffle Knit Top", "brand": "Northline Apparel", "desc": "A textured olive green waffle knit long-sleeve top with a relaxed fit and subtle ribbed cuffs.", "image": "assets/content/clothing/tops/NorthlineApparel/topWaffleKnitOlive.webp", "store": "storeClothingA", "slot": "top", "silhouette": "boxy", "baseLooks": 3, "price": 20, "quality": "Common", "warmth": 3, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_floral_chiffon_blush", "name": "Blush Pink Floral Chiffon Blouse", "brand": "Northline Apparel", "desc": "A lightweight dusty blush pink chiffon blouse with a delicate ditsy floral print, featuring subtle sheer sleeves and a keyhole neckline.", "image": "assets/content/clothing/tops/NorthlineApparel/topFloralChiffonBlush.webp", "store": "storeClothingA", "slot": "top", "silhouette": "draped", "baseLooks": 5, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 2, "exposureLevel": 1, "durability": 70, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_ribbed_crop_burgundy", "name": "Deep Burgundy Ribbed Crop Top", "brand": "Northline Apparel", "desc": "A short-sleeve deep burgundy ribbed knit crop top that sits just above the waistline, offering a snug, form-fitting silhouette.", "image": "assets/content/clothing/tops/NorthlineApparel/topRibbedCropBurgundy.webp", "store": "storeClothingA", "slot": "top", "silhouette": "cropped", "baseLooks": 5, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_tie_front_lemon", "name": "Lemon Yellow Tie-Front Blouse", "brand": "Northline Apparel", "desc": "A bright lemon yellow cotton poplin short-sleeve blouse featuring a functional tie-front knot detail at the hem.", "image": "assets/content/clothing/tops/NorthlineApparel/topTieFrontLemon.webp", "store": "storeClothingA", "slot": "top", "silhouette": "tie-front", "baseLooks": 6, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 75, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_pointelle_cream", "name": "Cream Pointelle Knit Cardigan Top", "brand": "Northline Apparel", "desc": "A soft cream pointelle knit fitted cardigan with delicate openwork texture and front buttons, worn fully buttoned up as a top.", "image": "assets/content/clothing/tops/NorthlineApparel/topPointelleCream.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 40, "quality": "Rare", "warmth": 2, "sexinessScore": 3, "exposureLevel": 2, "durability": 75, "reqCorruption": 1, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_off_shoulder_floral_blue", "name": "Light Blue Floral Off-Shoulder Top", "brand": "Northline Apparel", "desc": "A light pastel blue off-shoulder smocked top with a soft watercolor floral print and short ruffled sleeves, leaving shoulders bare.", "image": "assets/content/clothing/tops/NorthlineApparel/topOffShoulderFloralBlue.webp", "store": "storeClothingA", "slot": "top", "silhouette": "off-shoulder", "baseLooks": 6, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 6, "durability": 70, "reqCorruption": 3, "matchSet": null, "tags": ["cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 40, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_cowl_neck_satin_champagne", "name": "Champagne Satin Cowl Neck Cami", "brand": "Northline Apparel", "desc": "A delicate champagne-toned satin camisole top featuring a draped cowl neckline and thin adjustable spaghetti straps.", "image": "assets/content/clothing/tops/NorthlineApparel/topCowlNeckSatinChampagne.webp", "store": "storeClothingA", "slot": "top", "silhouette": "draped", "baseLooks": 7, "price": 45, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 75, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_boxy_cropped_charcoal", "name": "Charcoal Grey Boxy Crop Tee", "brand": "Northline Apparel", "desc": "A minimalist charcoal grey cotton jersey boxy crop tee with short wide sleeves and a high crew neckline.", "image": "assets/content/clothing/tops/NorthlineApparel/topBoxyCroppedCharcoal.webp", "store": "storeClothingA", "slot": "top", "silhouette": "boxy", "baseLooks": 5, "price": 15, "quality": "Common", "warmth": 1, "sexinessScore": 4, "exposureLevel": 2, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_slogan_hoodie_white", "name": "White Oversized Slogan Hoodie", "brand": "Northline Apparel", "desc": "A cozy, oversized white cotton fleece hoodie featuring a bold minimalist slogan print across the chest and a large front pocket.", "image": "assets/content/clothing/tops/NorthlineApparel/topSloganHoodieWhite.webp", "store": "storeClothingA", "slot": "top", "silhouette": "oversized", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 4, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_gingham_crop_rose", "name": "Rose Pink Gingham Crop Top", "brand": "Northline Apparel", "desc": "A cute rose pink and white gingham checked short-sleeve crop top with a lightly smocked back and square neckline.", "image": "assets/content/clothing/tops/NorthlineApparel/topGinghamCropRose.webp", "store": "storeClothingA", "slot": "top", "silhouette": "cropped", "baseLooks": 5, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 5, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_tie_dye_tee_pastel", "name": "Pastel Tie-Dye Relaxed Tee", "brand": "Northline Apparel", "desc": "A relaxed fit cotton jersey tee in a soft, washed pastel tie-dye swirl featuring muted tones of lavender, blue, and pink.", "image": "assets/content/clothing/tops/NorthlineApparel/topTieDyeTeePastel.webp", "store": "storeClothingA", "slot": "top", "silhouette": "oversized", "baseLooks": 3, "price": 20, "quality": "Common", "warmth": 0, "sexinessScore": 0, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_tropical_blouse_cream", "name": "Cream Tropical Pattern Blouse", "brand": "Northline Apparel", "desc": "A cream-colored lightweight woven blouse featuring a vibrant green and earthy brown tropical leaf print, with short rolled sleeves.", "image": "assets/content/clothing/tops/NorthlineApparel/topTropicalBlouseCream.webp", "store": "storeClothingA", "slot": "top", "silhouette": "boxy", "baseLooks": 4, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 0, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_ribbed_coord_top_grey", "name": "Heather Grey Ribbed Knit Top", "brand": "Northline Apparel", "desc": "A heather grey ribbed knit long-sleeve top with a flattering slim fit, designed to pair wonderfully with matching knit pieces.", "image": "assets/content/clothing/tops/NorthlineApparel/topRibbedCoordTopGrey.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 4, "price": 15, "quality": "Common", "warmth": 1, "sexinessScore": 2, "exposureLevel": 1, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_vneck_basic_black", "name": "Black Basic V-Neck Tee", "brand": "Northline Apparel", "desc": "A wardrobe staple deep matte black cotton blend short-sleeve t-shirt with a classic V-neckline and standard fit.", "image": "assets/content/clothing/tops/NorthlineApparel/topVNeckBasicBlack.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 3, "price": 10, "quality": "Common", "warmth": 0, "sexinessScore": 1, "exposureLevel": 1, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 5, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sweatshirt_mint", "name": "Mint Green Oversized Sweatshirt", "brand": "Northline Apparel", "desc": "An oversized crew neck sweatshirt in a fresh pastel mint green hue, made with thick soft fleece featuring dropped shoulders.", "image": "assets/content/clothing/tops/NorthlineApparel/topSweatshirtMint.webp", "store": "storeClothingA", "slot": "top", "silhouette": "oversized", "baseLooks": 3, "price": 25, "quality": "Common", "warmth": 3, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_polo_knit_navy", "name": "Navy Blue Knit Polo Top", "brand": "Northline Apparel", "desc": "A smart-casual navy blue fine-knit polo top with short sleeves, a classic ribbed collar, and a subtle three-button fastening.", "image": "assets/content/clothing/tops/NorthlineApparel/topPoloKnitNavy.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_halter_neck_pink", "name": "Hot Pink Halter Neck Top", "brand": "Northline Apparel", "desc": "A vibrant hot pink stretch-jersey halter neck top with a self-tie closure behind the neck, leaving the upper back exposed.", "image": "assets/content/clothing/tops/NorthlineApparel/topHalterNeckPink.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 7, "price": 70, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_wrap_blouse_white", "name": "White Linen Wrap Blouse", "brand": "Northline Apparel", "desc": "A breezy soft white linen-blend wrap blouse with a deep V-neckline, short fluttering sleeves, and a side tie fastening.", "image": "assets/content/clothing/tops/NorthlineApparel/topWrapBlouseWhite.webp", "store": "storeClothingA", "slot": "top", "silhouette": "wrap", "baseLooks": 6, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_broderie_anglaise_ivory", "name": "Ivory Broderie Anglaise Top", "brand": "Northline Apparel", "desc": "An elegant ivory cotton top featuring intricate broderie anglaise embroidery cutouts along the hem and sleeves, with a scalloped edge.", "image": "assets/content/clothing/tops/NorthlineApparel/topBroderieAnglaiseIvory.webp", "store": "storeClothingA", "slot": "top", "silhouette": "boxy", "baseLooks": 5, "price": 40, "quality": "Rare", "warmth": 1, "sexinessScore": 3, "exposureLevel": 4, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_graphic_tee_band_black", "name": "Faded Black Rock Graphic Tee", "brand": "Northline Apparel", "desc": "A washed faded black cotton crew-neck tee with an edgy vintage rock band graphic printed in muted colors across the front.", "image": "assets/content/clothing/tops/NorthlineApparel/topGraphicTeeBandBlack.webp", "store": "storeClothingA", "slot": "top", "silhouette": "oversized", "baseLooks": 4, "price": 20, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_wrap_cropped_navy", "name": "Navy Blue Cropped Wrap Top", "brand": "Northline Apparel", "desc": "A short-sleeve navy blue cotton wrap top with a deep V-neckline and a side tie detail that cinches the cropped silhouette.", "image": "assets/content/clothing/tops/NorthlineApparel/topWrapCroppedNavy.webp", "store": "storeClothingA", "slot": "top", "silhouette": "wrap", "baseLooks": 5, "price": 25, "quality": "Common", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_camisole_lace_trim_black", "name": "Black Lace-Trim Camisole", "brand": "Northline Apparel", "desc": "A slinky matte black satin camisole top featuring delicate eyelash lace trim along the V-neckline and thin adjustable straps.", "image": "assets/content/clothing/tops/NorthlineApparel/topCamisoleLaceTrimBlack.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 7, "price": 30, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 75, "reqCorruption": 3, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 45, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sweater_chunky_cream", "name": "Cream Chunky Knit Sweater", "brand": "Northline Apparel", "desc": "A cozy, oversized cream-colored chunky cable knit sweater with long voluminous sleeves and a high mock neckline.", "image": "assets/content/clothing/tops/NorthlineApparel/topSweaterChunkyCream.webp", "store": "storeClothingA", "slot": "top", "silhouette": "oversized", "baseLooks": 5, "price": 45, "quality": "Common", "warmth": 4, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_turtleneck_ribbed_camel", "name": "Camel Ribbed Turtleneck", "brand": "Northline Apparel", "desc": "A sleek form-fitting camel brown stretch-ribbed turtleneck top featuring long sleeves and an elegantly snug silhouette.", "image": "assets/content/clothing/tops/NorthlineApparel/topTurtleneckRibbedCamel.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 35, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_tank_ribbed_white", "name": "White Ribbed Tank Top", "brand": "Northline Apparel", "desc": "A basic slim-fit bright white ribbed cotton tank top featuring a scoop neckline and wide comfortable shoulder straps.", "image": "assets/content/clothing/tops/NorthlineApparel/topTankRibbedWhite.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 3, "price": 10, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_blouse_puff_sleeve_lilac", "name": "Lilac Puff Sleeve Blouse", "brand": "Northline Apparel", "desc": "A vibrant pastel lilac short-sleeve poplin blouse with distinct voluminous puff sleeves and a delicate sweetheart neckline.", "image": "assets/content/clothing/tops/NorthlineApparel/topBlousePuffSleeveLilac.webp", "store": "storeClothingA", "slot": "top", "silhouette": "boxy", "baseLooks": 6, "price": 30, "quality": "Common", "warmth": 1, "sexinessScore": 3, "exposureLevel": 2, "durability": 80, "reqCorruption": 0, "matchSet": null, "tags": ["cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_button_up_poplin_blue", "name": "Blue Striped Poplin Shirt", "brand": "Northline Apparel", "desc": "A crisp white and light blue vertical-striped poplin button-up shirt with long sleeves, worn casually with rolled-up cuffs.", "image": "assets/content/clothing/tops/NorthlineApparel/topButtonUpPoplinBlue.webp", "store": "storeClothingA", "slot": "top", "silhouette": "boxy", "baseLooks": 4, "price": 35, "quality": "Rare", "warmth": 2, "sexinessScore": 1, "exposureLevel": 1, "durability": 100, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_cardigan_cropped_plum", "name": "Plum Cropped Cardigan", "brand": "Northline Apparel", "desc": "A deep plum knitted short cardigan featuring long sleeves, tortoiseshell buttons, and a cropped fit that sits neatly at the waist.", "image": "assets/content/clothing/tops/NorthlineApparel/topCardiganCroppedPlum.webp", "store": "storeClothingA", "slot": "top", "silhouette": "cropped", "baseLooks": 5, "price": 30, "quality": "Common", "warmth": 3, "sexinessScore": 2, "exposureLevel": 2, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_tee_vneck_sage", "name": "Sage Green V-Neck Tee", "brand": "Northline Apparel", "desc": "A pale earthy sage green cotton-modal blend soft t-shirt featuring a subtle V-neckline and short sleeves, offering an easy everyday fit.", "image": "assets/content/clothing/tops/NorthlineApparel/topTeeVNeckSage.webp", "store": "storeClothingA", "slot": "top", "silhouette": "fitted", "baseLooks": 3, "price": 15, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_off_shoulder_knit_rust", "name": "Rust Off-Shoulder Knit Top", "brand": "Northline Apparel", "desc": "A warm rust-orange fine-knit long-sleeve top featuring a folded, wide off-shoulder neckline that elegantly exposes the collarbones and shoulders.", "image": "assets/content/clothing/tops/NorthlineApparel/topOffShoulderKnitRust.webp", "store": "storeClothingA", "slot": "top", "silhouette": "off-shoulder", "baseLooks": 6, "price": 40, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 4, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_satin_cowl_emerald", "name": "Emerald Satin Cowl Neck Top", "brand": "VERA Mode", "desc": "A luxurious deep emerald green satin sleeveless top featuring a beautifully draped cowl neckline and delicate thin strap detail.", "image": "assets/content/clothing/tops/VERAMode/topSatinCowlEmerald.webp", "store": "storeClothingB", "slot": "top", "silhouette": "draped", "baseLooks": 7, "price": 85, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 75, "reqCorruption": 2, "matchSet": null, "tags": ["sexy", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_ribbed_crop_terracotta", "name": "Terracotta Ribbed Cropped Top", "brand": "VERA Mode", "desc": "A fitted terracotta-toned ribbed jersey crop top with short sleeves and a seamless round neckline, with a modern cropped silhouette.", "image": "assets/content/clothing/tops/VERAMode/topRibbedCropTerracotta.webp", "store": "storeClothingB", "slot": "top", "silhouette": "cropped", "baseLooks": 5, "price": 20, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sheer_floral", "name": "Sheer Floral Print Blouse", "brand": "VERA Mode", "desc": "A delicate multi-toned sheer chiffon blouse with an all-over vintage floral print, long sleeves, and a gathered V-neckline.", "image": "assets/content/clothing/tops/VERAMode/topSheerFloral.webp", "store": "storeClothingB", "slot": "top", "silhouette": "draped", "baseLooks": 6, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 70, "reqCorruption": 3, "matchSet": null, "tags": ["cute", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_asym_hem_crop_red", "name": "Red Asymmetric Hem Crop Top", "brand": "VERA Mode", "desc": "A bold bright red stretch jersey crop top with a dramatically asymmetric hem one side cropping just above the waist, the other dipping lower.", "image": "assets/content/clothing/tops/VERAMode/topAsymHemCropRed.webp", "store": "storeClothingB", "slot": "top", "silhouette": "cropped", "baseLooks": 7, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 70, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_knot_front_camel", "name": "Camel Knot-Front Long Sleeve Top", "brand": "VERA Mode", "desc": "A fitted camel-colored long-sleeve jersey top with a stylish center-front knotted detail at the hem that reveals a hint of midriff.", "image": "assets/content/clothing/tops/VERAMode/topKnotFrontCamel.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 6, "price": 60, "quality": "Rare", "warmth": 1, "sexinessScore": 4, "exposureLevel": 3, "durability": 75, "reqCorruption": 1, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_off_shoulder_ribbed_navy", "name": "Navy Ribbed Off-Shoulder Top", "brand": "VERA Mode", "desc": "A fitted deep navy ribbed knit off-shoulder top with a wide elasticated neckline that sits just below the shoulders, leaving them fully bare.", "image": "assets/content/clothing/tops/VERAMode/topOffShoulderRibbedNavy.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 6, "price": 70, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 4, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_cutout_back_white", "name": "White Cut-Out Back Top", "brand": "VERA Mode", "desc": "A clean bright white short-sleeve fitted top with a striking geometric cut-out detail at the upper back, revealing a window of skin.", "image": "assets/content/clothing/tops/VERAMode/topCutoutBackWhite.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 80, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 5, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_leopard_print_crop", "name": "Leopard Print Satin Crop", "brand": "VERA Mode", "desc": "A sleek satin-finish leopard print crop top with thin spaghetti straps and a straight cut across the chest.", "image": "assets/content/clothing/tops/VERAMode/topLeopardPrintCrop.webp", "store": "storeClothingB", "slot": "top", "silhouette": "cropped", "baseLooks": 7, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 6, "durability": 75, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_velvet_slip_purple", "name": "Deep Purple Velvet Slip Top", "brand": "VERA Mode", "desc": "A rich deep purple velvet slip-style top with adjustable thin straps, a V front neckline, and a bias-cut silhouette with subtle sheen.", "image": "assets/content/clothing/tops/VERAMode/topVelvetSlipPurple.webp", "store": "storeClothingB", "slot": "top", "silhouette": "draped", "baseLooks": 7, "price": 90, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 4, "durability": 75, "reqCorruption": 2, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_corsage_blazer_cream", "name": "Cream Corsage-Detail Blazer Top", "brand": "VERA Mode", "desc": "A fitted cream structured blazer worn as a top, featuring a single oversized fabric flower corsage on the lapel for a statement look.", "image": "assets/content/clothing/tops/VERAMode/topCorsageBlazerCream.webp", "store": "storeClothingB", "slot": "top", "silhouette": "tailored", "baseLooks": 8, "price": 110, "quality": "Rare", "warmth": 1, "sexinessScore": 7, "exposureLevel": 6, "durability": 85, "reqCorruption": 4, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_zip_up_crop_red", "name": "Red Zip-Up Crop Top", "brand": "VERA Mode", "desc": "A bold cherry red stretch jersey long-sleeve crop top with a central front zip that can be partially undone for an edgy look.", "image": "assets/content/clothing/tops/VERAMode/topZipUpCropRed.webp", "store": "storeClothingB", "slot": "top", "silhouette": "cropped", "baseLooks": 7, "price": 70, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 6, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_pleated_silk_top_chamomile", "name": "Chamomile Pleated Silk Top", "brand": "VERA Mode", "desc": "A soft chamomile yellow silk-touch pleated blouse with a wide V-neck, relaxed boxy silhouette and front pleat detail.", "image": "assets/content/clothing/tops/VERAMode/topPleatedSilkTopChamomile.webp", "store": "storeClothingB", "slot": "top", "silhouette": "boxy", "baseLooks": 6, "price": 90, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 2, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_bandage_crop_neon", "name": "Neon Yellow Bandage Crop Top", "brand": "VERA Mode", "desc": "A striking neon yellow bandage-construction strapless crop top with horizontal rib bands forming a structural fitted silhouette.", "image": "assets/content/clothing/tops/VERAMode/topBandageCropNeon.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 6, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 85, "reqCorruption": 5, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_silk_blouse_rose_gold", "name": "Rose Gold Silk Open-Back Blouse", "brand": "VERA Mode", "desc": "A romantic rose gold silk long-sleeve blouse with a subtle open-back cross-strap detail and a classic front V-neck.", "image": "assets/content/clothing/tops/VERAMode/topSilkBlouseRoseGold.webp", "store": "storeClothingB", "slot": "top", "silhouette": "draped", "baseLooks": 8, "price": 95, "quality": "Rare", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 70, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 50, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_halter_neck_blazer_pink", "name": "Blush Pink Halter-Neck Blazer Top", "brand": "VERA Mode", "desc": "A structured blush pink blazer with a surprising halter-neck design no lapels, no collar, just shoulder structure and a halter tie at the neck.", "image": "assets/content/clothing/tops/VERAMode/topHalterNeckBlazerPink.webp", "store": "storeClothingB", "slot": "top", "silhouette": "structured", "baseLooks": 8, "price": 100, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_mesh_flared_sleeve_black", "name": "Black Mesh Flared-Sleeve Top", "brand": "VERA Mode", "desc": "A fitted matte black jersey top with dramatically flared sheer mesh sleeves that contrast with the opaque body fabric.", "image": "assets/content/clothing/tops/VERAMode/topMeshFlaredSleeveBlack.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 7, "price": 80, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 1, "durability": 70, "reqCorruption": 2, "matchSet": null, "tags": ["sexy", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_corset_bustier_beige", "name": "Beige Satin Corset Bustier", "brand": "VERA Mode", "desc": "A body-sculpting beige satin corset bustier top with structured boning, a sweetheart neckline, and hook-and-eye back closure.", "image": "assets/content/clothing/tops/VERAMode/topCorsetBustierBeige.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 8, "price": 95, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 80, "reqCorruption": 4, "matchSet": null, "tags": ["sexy", "revealing"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_wide_collar_knit", "name": "Cream Wide-Collar Knit Top", "brand": "VERA Mode", "desc": "A chic cream fine-knit top with an exaggerated wide flat sailor-style collar draping broadly across the shoulders.", "image": "assets/content/clothing/tops/VERAMode/topWideCollarKnit.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 6, "price": 75, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_flutter_sleeve_top_mint", "name": "Mint Flutter-Sleeve Satin Top", "brand": "VERA Mode", "desc": "A romantic mint green satin camisole top with delicate flutter sleeves that softly frame the shoulders and a gathered ruched front.", "image": "assets/content/clothing/tops/VERAMode/topFlutterSleeveTopMint.webp", "store": "storeClothingB", "slot": "top", "silhouette": "draped", "baseLooks": 7, "price": 80, "quality": "Rare", "warmth": 0, "sexinessScore": 6, "exposureLevel": 5, "durability": 75, "reqCorruption": 3, "matchSet": null, "tags": ["cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_ribbed_turtleneck_pink", "name": "Dusty Pink Ribbed Turtleneck", "brand": "VERA Mode", "desc": "A soft dusty pink ribbed fine-knit turtleneck long-sleeve fitted top with a form-sculpting silhouette and plush neck roll.", "image": "assets/content/clothing/tops/VERAMode/topRibbedTurtleneckPink.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 65, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_strappy_crop_cobalt", "name": "Cobalt Blue Strappy Crop Top", "brand": "VERA Mode", "desc": "A vibrant cobalt blue stretch-jersey crop top featuring multiple thin spaghetti straps forming an elegant woven effect across the front.", "image": "assets/content/clothing/tops/VERAMode/topStrappyCropCobalt.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 8, "price": 65, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 75, "reqCorruption": 4, "matchSet": null, "tags": ["revealing", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_ruched_sides_camel", "name": "Camel Side-Ruched Jersey Top", "brand": "VERA Mode", "desc": "A stretchy camel-tone jersey top with a round neck and short flutter sleeves, heavily gathered and ruched along both side seams.", "image": "assets/content/clothing/tops/VERAMode/topRuchedSidesCamel.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 60, "quality": "Rare", "warmth": 0, "sexinessScore": 3, "exposureLevel": 1, "durability": 80, "reqCorruption": 1, "matchSet": null, "tags": ["sexy", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_bow_detail_blouse_ivory", "name": "Ivory Bow-Detail Blouse", "brand": "VERA Mode", "desc": "A delicate ivory chiffon long-sleeve blouse featuring a large, statement self-fabric bow tied at the neckline.", "image": "assets/content/clothing/tops/VERAMode/topBowDetailBlouseIvory.webp", "store": "storeClothingB", "slot": "top", "silhouette": "draped", "baseLooks": 6, "price": 85, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 4, "durability": 90, "reqCorruption": 4, "matchSet": null, "tags": ["cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sequin_tube_black", "name": "Black Sequin Tube Top", "brand": "VERA Mode", "desc": "A dazzling all-over black sequin-encrusted strapless tube top, fully sequined from band to band with tight stretch structure.", "image": "assets/content/clothing/tops/VERAMode/topSequinTubeBlack.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 8, "price": 95, "quality": "Rare", "warmth": 0, "sexinessScore": 8, "exposureLevel": 7, "durability": 70, "reqCorruption": 4, "matchSet": null, "tags": ["revealing", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 60, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_directional_knit_greige", "name": "Greige Directional Knit Top", "brand": "VERA Mode", "desc": "A sophisticated light greige-tone directional stitch-pattern long-sleeve knit top with a subtle V-neckline.", "image": "assets/content/clothing/tops/VERAMode/topDirectionalKnitGreige.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 65, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_crown_neck_corset_silver_grey", "name": "Silver Grey Corset Neck Top", "brand": "VERA Mode", "desc": "A metallic silver-grey lace-up corset-neck long-sleeve top featuring visible laces spiraling down from a high crown neckline.", "image": "assets/content/clothing/tops/VERAMode/topCrownNeckCorsetSilverGrey.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 8, "price": 80, "quality": "Rare", "warmth": 0, "sexinessScore": 9, "exposureLevel": 8, "durability": 80, "reqCorruption": 5, "matchSet": null, "tags": ["sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 75, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_floral_jacquard_terracotta", "name": "Terracotta Jacquard Floral Top", "brand": "VERA Mode", "desc": "A terracotta-toned jacquard woven top featuring an intricate raised tonal floral motif weaved into the fabric structure, with short sleeves.", "image": "assets/content/clothing/tops/VERAMode/topFloralJacquardTerracotta.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 90, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["casual", "cute"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_mesh_body_con_black", "name": "Black Mesh Bodycon Long-Sleeve Top", "brand": "VERA Mode", "desc": "A fully sheer black mesh long-sleeve fitted top with a high neckline, worn over a supportive seamless black bralette visible beneath.", "image": "assets/content/clothing/tops/VERAMode/topMeshBodyConBlack.webp", "store": "storeClothingB", "slot": "top", "silhouette": "fitted", "baseLooks": 8, "price": 75, "quality": "Rare", "warmth": 0, "sexinessScore": 9, "exposureLevel": 8, "durability": 65, "reqCorruption": 5, "matchSet": null, "tags": ["revealing", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 80, "reqExhibitionism": 40, "reqHeelsSkill": 0},
{"id": "top_houndstooth_crop_white_black", "name": "Houndstooth Cropped Top", "brand": "VERA Mode", "desc": "A sharp geometric black and white houndstooth print structured crop top with short sleeves and a wide square neckline.", "image": "assets/content/clothing/tops/VERAMode/topHoundstoothCropWhiteBlack.webp", "store": "storeClothingB", "slot": "top", "silhouette": "cropped", "baseLooks": 6, "price": 70, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 4, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["cute", "casual"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_structured_blazer_charcoal", "name": "Charcoal Wool-Blend Blazer Top", "brand": "Fifth Avenue Wear", "desc": "An impeccably tailored deep charcoal wool-blend blazer worn buttoned up as a structural top, featuring rigid sharp shoulders and fine double-breasted buttons.", "image": "assets/content/clothing/tops/FifthAvenueWear/topStructuredBlazerCharcoal.webp", "store": "storeClothingC", "slot": "top", "silhouette": "tailored", "baseLooks": 8, "price": 180, "quality": "Premium", "warmth": 1, "sexinessScore": 5, "exposureLevel": 3, "durability": 90, "reqCorruption": 2, "matchSet": null, "tags": ["professional", "elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_crepe_silk_navy_high", "name": "Navy Silk-Crepe Structured Top", "brand": "Fifth Avenue Wear", "desc": "A premium navy blue silk-crepe long-sleeve structured top with a perfectly upright high collar and clean knife-edge pleating at the torso.", "image": "assets/content/clothing/tops/FifthAvenueWear/topCrepeSilkNavyHigh.webp", "store": "storeClothingC", "slot": "top", "silhouette": "structured", "baseLooks": 6, "price": 195, "quality": "Premium", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_tailored_tuxedo_white", "name": "Ivory Tailored Tuxedo Top", "brand": "Fifth Avenue Wear", "desc": "A sharp structured ivory cotton-poplin button-front tailored tuxedo shirt worn as an elegant boxy top, featuring a satin-edged narrow lapel.", "image": "assets/content/clothing/tops/FifthAvenueWear/topTailoredTuxedoWhite.webp", "store": "storeClothingC", "slot": "top", "silhouette": "tailored", "baseLooks": 7, "price": 150, "quality": "Premium", "warmth": 1, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["formal", "elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_scuba_rollneck_black", "name": "Black Scuba Roll-Neck Top", "brand": "Fifth Avenue Wear", "desc": "A sleek structured black scuba-fabric long-sleeve top with a high roll neck and a perfectly smooth, wrinkle-free surface.", "image": "assets/content/clothing/tops/FifthAvenueWear/topScubaRollneckBlack.webp", "store": "storeClothingC", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 130, "quality": "Premium", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_asym_satin_blouse_midnight", "name": "Midnight Blue Asymmetric Satin Blouse", "brand": "Fifth Avenue Wear", "desc": "A formal midnight blue satin blouse with an architecturally asymmetric neckline, a single concealed pleat, and sculptural oversized cuffs.", "image": "assets/content/clothing/tops/FifthAvenueWear/topAsymSatinBlouseMidnight.webp", "store": "storeClothingC", "slot": "top", "silhouette": "structured", "baseLooks": 8, "price": 200, "quality": "Premium", "warmth": 1, "sexinessScore": 4, "exposureLevel": 3, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_pearl_button_tailored_wine", "name": "Wine Red Pearl-Button Blouse", "brand": "Fifth Avenue Wear", "desc": "A rich wine-red elevated blouse with precise tailored seaming, polished pearl buttons down the front, and neatly folded French cuffs.", "image": "assets/content/clothing/tops/FifthAvenueWear/topPearlButtonTailoredWine.webp", "store": "storeClothingC", "slot": "top", "silhouette": "tailored", "baseLooks": 7, "price": 155, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_silhouette_satin_wrap", "name": "Champagne Satin Power Wrap", "brand": "Fifth Avenue Wear", "desc": "A sophisticated champagne-hued heavy-satin long-sleeved wrap top with an elevated V-neckline and structured padded shoulders for a powerful silhouette.", "image": "assets/content/clothing/tops/FifthAvenueWear/topSilhouetteSatinWrap.webp", "store": "storeClothingC", "slot": "top", "silhouette": "structured", "baseLooks": 8, "price": 145, "quality": "Premium", "warmth": 1, "sexinessScore": 6, "exposureLevel": 4, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_knit_crew_fine_gold", "name": "Gold Fine-Knit Crew Top", "brand": "Fifth Avenue Wear", "desc": "A warm burnished-gold fine-gauge knit long-sleeve crew top featuring subtle vertical ribbed channels and a refined slim cut.", "image": "assets/content/clothing/tops/FifthAvenueWear/topKnitCrewFineGold.webp", "store": "storeClothingC", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 95, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_leather_trim_black", "name": "Black Leather-Trim Ponte Top", "brand": "Fifth Avenue Wear", "desc": "A sophisticated matte black ponte knit sleeveless structured top with refined faux-leather trim detailing at the V-neckline and armholes.", "image": "assets/content/clothing/tops/FifthAvenueWear/topLeatherTrimBlack.webp", "store": "storeClothingC", "slot": "top", "silhouette": "structured", "baseLooks": 7, "price": 165, "quality": "Premium", "warmth": 1, "sexinessScore": 4, "exposureLevel": 2, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 15, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_drape_boatneck_ecru", "name": "Ecru Drape Boatneck Top", "brand": "Fifth Avenue Wear", "desc": "A luxuriously soft ecru-white viscose long-sleeve top with a wide shallow bateau neckline and front drape detail cascading beautifully across the torso.", "image": "assets/content/clothing/tops/FifthAvenueWear/topDrapeBoatneckEcru.webp", "store": "storeClothingC", "slot": "top", "silhouette": "draped", "baseLooks": 6, "price": 145, "quality": "Rare", "warmth": 2, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_wool_blend_oatmeal", "name": "Oatmeal Wool-Blend Fitted Top", "brand": "Fifth Avenue Wear", "desc": "A slim, tailored oatmeal-toned wool-blend sleeveless top with princess seaming that sculpts a refined, polished silhouette.", "image": "assets/content/clothing/tops/FifthAvenueWear/topWoolBlendOatmeal.webp", "store": "storeClothingC", "slot": "top", "silhouette": "tailored", "baseLooks": 6, "price": 95, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 2, "durability": 90, "reqCorruption": 1, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_structured_crepe_peplum_red", "name": "Red Crepe Structured Peplum Top", "brand": "Fifth Avenue Wear", "desc": "A statement bold true red structured crepe short-sleeve peplum top with a precision-cut flared ruffle at the waist.", "image": "assets/content/clothing/tops/FifthAvenueWear/topStructuredCrepePeplumRed.webp", "store": "storeClothingC", "slot": "top", "silhouette": "fitted", "baseLooks": 7, "price": 170, "quality": "Premium", "warmth": 1, "sexinessScore": 3, "exposureLevel": 1, "durability": 85, "reqCorruption": 1, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_satin_amethyst", "name": "Amethyst Satin Sleeveless Top", "brand": "Fifth Avenue Wear", "desc": "A rich amethyst purple satin sleeveless cowl-back top with visible back draping and a classic structured front.", "image": "assets/content/clothing/tops/FifthAvenueWear/topSatinAmethyst.webp", "store": "storeClothingC", "slot": "top", "silhouette": "draped", "baseLooks": 8, "price": 175, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 70, "reqCorruption": 4, "matchSet": null, "tags": ["elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_jersey_italian_blue", "name": "Italian Blue Jersey Structured Long-Sleeve", "brand": "Fifth Avenue Wear", "desc": "A classic refined Italian blue fine-jersey long-sleeve top with distinct contrast stitching and a modern slim fit.", "image": "assets/content/clothing/tops/FifthAvenueWear/topJerseyItalianBlue.webp", "store": "storeClothingC", "slot": "top", "silhouette": "fitted", "baseLooks": 4, "price": 85, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_cashmere_blend_cream", "name": "Cream Cashmere-Blend V-Neck", "brand": "Fifth Avenue Wear", "desc": "A precious cream fine-gauge cashmere-blend long-sleeve V-neck top with an ultra-soft hand-feel and naturally elegant drape.", "image": "assets/content/clothing/tops/FifthAvenueWear/topCashmereBlendCream.webp", "store": "storeClothingC", "slot": "top", "silhouette": "fitted", "baseLooks": 6, "price": 200, "quality": "Premium", "warmth": 3, "sexinessScore": 2, "exposureLevel": 1, "durability": 95, "reqCorruption": 0, "matchSet": null, "tags": ["elegant", "professional"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_geometric_print_blouse_sage", "name": "Sage Geometric Print Blouse", "brand": "Fifth Avenue Wear", "desc": "A refined sage green long-sleeve blouse featuring a subtle tonal geometric brushstroke print and an elevated elongated placket detail.", "image": "assets/content/clothing/tops/FifthAvenueWear/topGeometricPrintBlouseSage.webp", "store": "storeClothingC", "slot": "top", "silhouette": "tailored", "baseLooks": 5, "price": 155, "quality": "Rare", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_silk_jersey_wrap_coral_red", "name": "Coral Red Silk-Jersey Wrap Top", "brand": "Fifth Avenue Wear", "desc": "A luxurious coral-red silk-jersey wrap top with an elegant extended deep V-neckline, long column sleeves, and a flowing side drape.", "image": "assets/content/clothing/tops/FifthAvenueWear/topSilkJerseyWrapCoralRed.webp", "store": "storeClothingC", "slot": "top", "silhouette": "wrap", "baseLooks": 7, "price": 95, "quality": "Rare", "warmth": 1, "sexinessScore": 5, "exposureLevel": 3, "durability": 80, "reqCorruption": 3, "matchSet": null, "tags": ["elegant", "sexy"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_formal_button_blouse_blush", "name": "Blush Formal Button-Through Blouse", "brand": "Fifth Avenue Wear", "desc": "A refined dusty blush long-sleeve formal blouse with concealed button closure, smooth crepe fabric, and elegant French tucked hem.", "image": "assets/content/clothing/tops/FifthAvenueWear/topFormalButtonBlouseBlush.webp", "store": "storeClothingC", "slot": "top", "silhouette": "tailored", "baseLooks": 5, "price": 150, "quality": "Rare", "warmth": 2, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["professional", "elegant"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_silk_halter_deep_crimson", "name": "Crimson Silk Halter Top", "brand": "Fifth Avenue Wear", "desc": "A premium deep crimson pure-silk halter top with self-tied neck straps, a bias-cut drape across the bodice, and a plunging open back.", "image": "assets/content/clothing/tops/FifthAvenueWear/topSilkHalterDeepCrimson.webp", "store": "storeClothingC", "slot": "top", "silhouette": "draped", "baseLooks": 8, "price": 220, "quality": "Premium", "warmth": 0, "sexinessScore": 7, "exposureLevel": 7, "durability": 70, "reqCorruption": 4, "matchSet": null, "tags": ["revealing", "elegant", "formal"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 55, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sports_mesh_tank_seafoam", "name": "Seafoam Mesh-Panel Racerback Tank", "brand": "FastBreak Athletics", "desc": "A sporty seafoam green moisture-wicking racerback tank top featuring engineered mesh side panels for ventilation and a fitted athletic silhouette.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsMeshTankSeafoam.webp", "store": "storeSports", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 35, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 3, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 20, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sports_zip_hoodie_cobalt", "name": "Cobalt Half-Zip Running Hoodie", "brand": "FastBreak Athletics", "desc": "A vibrant cobalt blue lightweight half-zip pullover running hoodie with a fitted hood and thumb-hole long sleeves.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsZipHoodieCobalt.webp", "store": "storeSports", "slot": "top", "silhouette": "fitted", "baseLooks": 3, "price": 55, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 1, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sports_crop_bralet", "name": "Black Sports Bra Crop Top", "brand": "FastBreak Athletics", "desc": "A structured black medium-impact sports bra crop top featuring a scoop neckline, racerback, and full-cup supportive construction.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsCropBralet.webp", "store": "storeSports", "slot": "top", "silhouette": "cropped", "baseLooks": 4, "price": 40, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 85, "reqCorruption": 2, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 30, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sports_long_sleeve_compression", "name": "Navy Compression Long-Sleeve Top", "brand": "FastBreak Athletics", "desc": "A deep navy blue athletic compression long-sleeve top with reflective logo details and flatlock seaming for reduced friction.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsLongSleeveCompression.webp", "store": "storeSports", "slot": "top", "silhouette": "fitted", "baseLooks": 4, "price": 50, "quality": "Rare", "warmth": 1, "sexinessScore": 2, "exposureLevel": 1, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sports_tank_color_block_orange", "name": "Orange Color-Block Training Tank", "brand": "FastBreak Athletics", "desc": "A sleeveless training tank in a bold orange and white color-block design with contrast piping along the shoulder seams.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsTankColorBlockOrange.webp", "store": "storeSports", "slot": "top", "silhouette": "fitted", "baseLooks": 3, "price": 30, "quality": "Common", "warmth": 0, "sexinessScore": 2, "exposureLevel": 2, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 10, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sports_zip_track_jacket", "name": "Black Full-Zip Track Jacket", "brand": "FastBreak Athletics", "desc": "A sleek black poly-blend full-zip track jacket with a mock collar, contrast white side stripe, and lightweight bonded construction.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsZipTrackJacket.webp", "store": "storeSports", "slot": "top", "silhouette": "boxy", "baseLooks": 4, "price": 60, "quality": "Rare", "warmth": 1, "sexinessScore": 1, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sports_crop_hoodie_grey", "name": "Heather Grey Sports Crop Hoodie", "brand": "FastBreak Athletics", "desc": "A cozy heather grey fleece-lined athletic crop hoodie with a drawstring hood and kangaroo pocket, hitting just above the waistband.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsCropHoodieGrey.webp", "store": "storeSports", "slot": "top", "silhouette": "cropped", "baseLooks": 3, "price": 45, "quality": "Common", "warmth": 3, "sexinessScore": 2, "exposureLevel": 3, "durability": 85, "reqCorruption": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id":"top_sports_bra_crop_rose","name":"Rose Sports Bra Strappy Top","brand":"FastBreak Athletics","desc":"A stretchy rose pink strappy-design sports bra crop top with multiple cross-back straps creating a cage-back effect and strong medium support.","image":"assets/content/clothing/tops/FastBreakAthletics/topSportsBraCropRose.webp","store":"storeSports","slot":"top","silhouette":"cropped","baseLooks":4,"price":40,"quality":"Rare","warmth":0,"sexinessScore":5,"exposureLevel":6,"durability":100,"reqCorruption":0,"matchSet":null,"tags":["sporty"],"npcAppeal":{},"shopAvailable":true,"startOwned":false},
{"id": "top_sports_windbreaker_neon", "name": "Neon Green Lightweight Windbreaker", "brand": "FastBreak Athletics", "desc": "An electric neon lime green ultralight zip-up windbreaker jacket with a packable hood and taped seams, striking and highly visible.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsWindbreakerNeon.webp", "store": "storeSports", "slot": "top", "silhouette": "boxy", "baseLooks": 4, "price": 70, "quality": "Rare", "warmth": 0, "sexinessScore": 4, "exposureLevel": 4, "durability": 65, "reqCorruption": 3, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 25, "reqExhibitionism": 30, "reqHeelsSkill": 0},
{"id": "top_sports_mesh_back_yoga", "name": "Teal Mesh-Back Yoga Top", "brand": "FastBreak Athletics", "desc": "A flattering teal-toned athletic fitted long-sleeve yoga top with an open-mesh panel covering most of the back for ventilation and a stylish look.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsMeshBackYoga.webp", "store": "storeSports", "slot": "top", "silhouette": "fitted", "baseLooks": 5, "price": 50, "quality": "Rare", "warmth": 0, "sexinessScore": 5, "exposureLevel": 5, "durability": 80, "reqCorruption": 2, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 35, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "top_sports_fleece_pullover_purple", "name": "Purple Sports Fleece Pullover", "brand": "FastBreak Athletics", "desc": "A warm deep purple athletic fleece pullover with a quarter-zip closure, a subtle textured surface, and ribbed athletic cuffs.", "image": "assets/content/clothing/tops/FastBreakAthletics/topSportsFleecePulloverPurple.webp", "store": "storeSports", "slot": "top", "silhouette": "boxy", "baseLooks": 3, "price": 55, "quality": "Common", "warmth": 4, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["sporty"], "npcAppeal": {}, "shopAvailable": true, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0},
{"id": "topRubysDishwash", "name": "Ruby's Diner Dishwasher Top", "brand": "", "desc": "Heavy cotton work top issued for dishwasher shifts at Ruby's Diner. Fitted, short sleeves, durable.", "image": "assets/content/clothing/others/RubysDiner/topRubysDishwash.webp", "store": "", "slot": "top", "silhouette": "fitted", "baseLooks": 1, "price": 0, "quality": "Common", "warmth": 1, "sexinessScore": 0, "exposureLevel": 0, "durability": 90, "reqCorruption": 0, "matchSet": null, "tags": ["professional"], "npcAppeal": {}, "shopAvailable": false, "startOwned": false, "reqConfidence": 0, "reqExhibitionism": 0, "reqHeelsSkill": 0}
]>>/*
WIDGET: checkClothingRequirements
Input: _wardrobeItemToCheck (The clothing item object)
Output: Sets _wardrobeCheckResult to { allowed: boolean, reason: string }
*/
<<widget "checkClothingRequirements">>
<<set _item to _wardrobeItemToCheck>>
<<set _checkResult to { allowed: true, reason: "" }>>
<<if _item>>
/* 1) Baseline from sexiness/exposure (new spec). Fallback to legacy tags if scores missing. */
<<set _exposure to null>>
<<set _sexiness to null>>
<<if def _item.exposureLevel>><<set _exposure to _item.exposureLevel>><</if>>
<<if def _item.sexinessScore>><<set _sexiness to _item.sexinessScore>><</if>>
<<if _exposure is null or _sexiness is null>>
<<set _tagExposure to 0>>
<<set _tagSexiness to 0>>
<<if _item.tags>>
<<if _item.tags.includes("mild")>>
<<set _tagExposure to Math.max(_tagExposure, 2)>>
<<set _tagSexiness to Math.max(_tagSexiness, 2)>>
<</if>>
<<if _item.tags.includes("revealing")>>
<<set _tagExposure to Math.max(_tagExposure, 4)>>
<<set _tagSexiness to Math.max(_tagSexiness, 4)>>
<</if>>
<<if _item.tags.includes("daring")>>
<<set _tagExposure to Math.max(_tagExposure, 6)>>
<<set _tagSexiness to Math.max(_tagSexiness, 5)>>
<</if>>
<<if _item.tags.includes("bold")>>
<<set _tagExposure to Math.max(_tagExposure, 8)>>
<<set _tagSexiness to Math.max(_tagSexiness, 7)>>
<</if>>
<<if _item.tags.includes("erotic")>>
<<set _tagExposure to Math.max(_tagExposure, 9)>>
<<set _tagSexiness to Math.max(_tagSexiness, 8)>>
<</if>>
<<if _item.tags.includes("lewd")>>
<<set _tagExposure to Math.max(_tagExposure, 10)>>
<<set _tagSexiness to Math.max(_tagSexiness, 9)>>
<</if>>
<</if>>
<<if _exposure is null>><<set _exposure to _tagExposure>><</if>>
<<if _sexiness is null>><<set _sexiness to _tagSexiness>><</if>>
<</if>>
<<set _reqConf to Math.max(0, Math.min(100, (_exposure * 7) + (_sexiness * 3)))>>
<<set _reqExh to Math.max(0, (_exposure - 3) * 6)>>
<<set _reqCorr to (_exposure >= 7 ? (_exposure - 4) : 0)>>
/* Condition penalties from runtime state */
<<set _runtimeItemState to null>>
<<if def $wardrobe.itemState and def _item.id and def $wardrobe.itemState[_item.id]>>
<<set _runtimeItemState to $wardrobe.itemState[_item.id]>>
<</if>>
<<if _runtimeItemState>>
<<if def _runtimeItemState.durability and _runtimeItemState.durability < 30>>
<<set _reqConf += 10>>
<</if>>
<<if def _runtimeItemState.dirt and _runtimeItemState.dirt > 60>>
<<set _reqConf += 5>>
<</if>>
<<set _reqConf = Math.max(0, Math.min(100, _reqConf))>>
<</if>>
/* 2. Apply Item-Specific Overrides (The Hybrid Part) */
<<if def _item.reqConfidence>>
<<set _reqConf to _item.reqConfidence>>
<</if>>
<<if def _item.reqExhibitionism>>
<<set _reqExh to _item.reqExhibitionism>>
<</if>>
<<if def _item.reqCorruption>>
<<set _reqCorr to _item.reqCorruption>>
<</if>>
/* 3. Perform Checks Against Player Stats */
/* Initialize stats if missing to prevent errors */
<<if ndef $confidence>><<set $confidence to 0>><</if>>
<<if ndef $exhibitionism>><<set $exhibitionism to 0>><</if>>
<<if ndef $corruption>><<set $corruption to 0>><</if>>
<<if $confidence < _reqConf>>
<<set _reason to "Requires " + _reqConf + " Confidence">>
<<if def _item.reqHeelsSkill>><<set _reason to _reason + ", " + _item.reqHeelsSkill + " Heels skill">><</if>>
<<set _checkResult = { allowed: false, reason: _reason }>>
<<elseif $exhibitionism < _reqExh>>
<<set _reason to "Requires " + _reqExh + " Exhibitionism">>
<<if def _item.reqHeelsSkill>><<set _reason to _reason + ", " + _item.reqHeelsSkill + " Heels skill">><</if>>
<<set _checkResult = { allowed: false, reason: _reason }>>
<<elseif $corruption < _reqCorr>>
<<set _reason to "Requires " + _reqCorr + " Corruption">>
<<if def _item.reqHeelsSkill>><<set _reason to _reason + ", " + _item.reqHeelsSkill + " Heels skill">><</if>>
<<set _checkResult = { allowed: false, reason: _reason }>>
<<elseif def _item.reqHeelsSkill>>
<<if ndef $skills.physical.heels>><<set $skills.physical.heels to 0>><</if>>
<<if $skills.physical.heels < _item.reqHeelsSkill>>
<<set _reason to "Requires " + _item.reqHeelsSkill + " Heels skill">>
<<if _reqConf > 0>><<set _reason to _reason + ", " + _reqConf + " Confidence">><</if>>
<<set _checkResult = { allowed: false, reason: _reason }>>
<</if>>
<</if>>
<</if>>
<<set _wardrobeCheckResult to _checkResult>>
<</widget>>
/*
WIDGET: checkCommandoRequirement
Input: _wardrobeSlotToCheck ('panty' or 'bra')
Output: Sets _wardrobeCheckResult to { allowed: boolean, reason: string }
*/
<<widget "checkCommandoRequirement">>
<<set _slot to _wardrobeSlotToCheck>>
<<set _checkResult to { allowed: true, reason: "" }>>
<<set _hasBodysuit to ($wardrobe.equipped.bodysuit and $wardrobe.equipped.bodysuit !== "")>>
<<if _slot is "panty">>
<<if _hasBodysuit>>
<<set _wardrobeCheckResult to _checkResult>>
<<return>>
<</if>>
<<set _coverLevel to "nude">>
/* Check what covers the bottom */
<<if $wardrobe.equipped.dress>>
/* Dresses are risky (Level 2) */
<<set _coverLevel to "risky">>
/* Unless sheer (Level 3) - Logic can be expanded here */
<<elseif $wardrobe.equipped.bottom>>
<<set _bottomItem to setup.getClothingById($wardrobe.equipped.bottom)>>
<<if _bottomItem>>
<<if _bottomItem.tags.includes("skirt")>>
<<set _coverLevel to "risky">>
<<elseif _bottomItem.tags.includes("sheer") or _bottomItem.tags.includes("cutout")>>
<<set _coverLevel to "exposed">>
<<else>>
<<set _coverLevel to "safe">>
<</if>>
<</if>>
<</if>>
/* Define Thresholds */
<<if _coverLevel is "safe" and $exhibitionism < 5>>
<<set _checkResult = { allowed: false, reason: "Requires 5 Exhibitionism to go commando (hidden)." }>>
<<elseif _coverLevel is "risky" and $exhibitionism < 10>>
<<set _checkResult = { allowed: false, reason: "Requires 10 Exhibitionism to go commando (skirt/dress)." }>>
<<elseif _coverLevel is "exposed" and $exhibitionism < 15>>
<<set _checkResult = { allowed: false, reason: "Requires 15 Exhibitionism to go commando (visible)." }>>
<<elseif _coverLevel is "nude" and $exhibitionism < 20>>
<<set _checkResult = { allowed: false, reason: "Requires 20 Exhibitionism to go commando (no cover)." }>>
<</if>>
<<elseif _slot is "bra">>
<<if _hasBodysuit>>
<<set _wardrobeCheckResult to _checkResult>>
<<return>>
<</if>>
/* BRA LOGIC */
<<set _coverLevel to "nude">>
/* Check what covers the top */
<<if $wardrobe.equipped.dress>>
<<set _dressItem to setup.getClothingById($wardrobe.equipped.dress)>>
<<if _dressItem>>
<<if _dressItem.tags.includes("sheer") or _dressItem.tags.includes("cutout")>>
<<set _coverLevel to "exposed">>
<<else>>
<<set _coverLevel to "risky">>
<</if>>
<</if>>
<<elseif $wardrobe.equipped.top>>
<<set _topItem to setup.getClothingById($wardrobe.equipped.top)>>
<<if _topItem>>
<<if _topItem.tags.includes("sheer") or _topItem.tags.includes("cutout")>>
<<set _coverLevel to "exposed">>
<<elseif _topItem.tags.includes("crop") or _topItem.tags.includes("tank")>>
<<set _coverLevel to "risky">>
<<else>>
<<set _coverLevel to "safe">>
<</if>>
<</if>>
<</if>>
/* Define Thresholds */
<<if _coverLevel is "safe" and $exhibitionism < 3>>
<<set _checkResult = { allowed: false, reason: "Requires 3 Exhibitionism to go braless (hidden)." }>>
<<elseif _coverLevel is "risky" and $exhibitionism < 7>>
<<set _checkResult = { allowed: false, reason: "Requires 7 Exhibitionism to go braless (crop/tank)." }>>
<<elseif _coverLevel is "exposed" and $exhibitionism < 12>>
<<set _checkResult = { allowed: false, reason: "Requires 12 Exhibitionism to go braless (visible)." }>>
<<elseif _coverLevel is "nude" and $exhibitionism < 18>>
<<set _checkResult = { allowed: false, reason: "Requires 18 Exhibitionism to go braless (no cover)." }>>
<</if>>
<</if>>
<<set _wardrobeCheckResult to _checkResult>>
<</widget>>
/* Helper to expose JS function to Twee if needed */
<<script>>
setup.getClothingById = function(id) {
if (window.getSetup && window.getSetup().clothingData) {
let items = [];
Object.values(window.getSetup().clothingData).forEach(arr => items = items.concat(arr));
return items.find(i => i.id === id);
}
return null;
}
<</script>>
/*
WIDGET: checkBedroomExit
Bedroom → Upstairs (within house)
- Fully dressed : free
- Sleepwear (non-sexy <=2) : free
- Sleepwear (sexy >=3) : Corruption 3+
- Underwear only : Corruption 4+
- Naked : Corruption 5+ AND Exhibitionism 10+
*/
<<widget "checkBedroomExit">>
<<if $_navigatingBackward>><<return>><</if>>
<<set _canExit to false>>
<<set _reason to "">>
/* Safeguards */
<<if !$wardrobe>><<set $wardrobe = { equipped: {} }>><</if>>
<<if !$wardrobe.equipped>><<set $wardrobe.equipped = {} >><</if>>
<<if ndef $corruption>><<set $corruption = 0>><</if>>
<<if ndef $exhibitionism>><<set $exhibitionism = 0>><</if>>
/* Coverage flags */
<<set _hasBodysuit to ($wardrobe.equipped.bodysuit and $wardrobe.equipped.bodysuit !== "")>>
<<set _hasTop to (($wardrobe.equipped.top and $wardrobe.equipped.top !== "") or _hasBodysuit)>>
<<set _hasBottom to ($wardrobe.equipped.bottom and $wardrobe.equipped.bottom !== "")>>
<<set _hasDress to ($wardrobe.equipped.dress and $wardrobe.equipped.dress !== "")>>
<<set _hasBra to (($wardrobe.equipped.bra and $wardrobe.equipped.bra !== "") or _hasBodysuit)>>
<<set _hasPanty to ($wardrobe.equipped.panty and $wardrobe.equipped.panty !== "")>>
<<set _hasSleepwear to ($wardrobe.equipped.sleepwear and $wardrobe.equipped.sleepwear !== "")>>
/* Categories */
<<set _isFullyDressed to (_hasDress or ((_hasTop or _hasBodysuit) and _hasBottom))>>
<<set _isInSleepwear to (_hasSleepwear and !_isFullyDressed)>>
<<set _isUnderwearOnly to (!_isFullyDressed and !_hasSleepwear and ((_hasBra or _hasPanty) or _hasBodysuit))>>
<<set _isNaked to (!_isFullyDressed and !_hasSleepwear and !_hasBra and !_hasPanty and !_hasBodysuit)>>
/* Sleepwear sexiness */
<<set _sleepwearSexiness to 0>>
<<if _isInSleepwear>>
<<set _swItem to setup.getClothingById($wardrobe.equipped.sleepwear)>>
<<if _swItem>><<set _sleepwearSexiness to (_swItem.sexinessScore || 0)>><</if>>
<</if>>
/* Decision */
<<if _isFullyDressed>>
<<set _canExit to true>>
<<elseif _isInSleepwear>>
<<if _sleepwearSexiness >= 3>>
<<if $corruption >= 3>>
<<set _canExit to true>>
<<else>>
<<set _reason to "That sleepwear is too revealing to walk out in. (Corruption 3 required)">>
<</if>>
<<else>>
<<set _canExit to true>>
<</if>>
<<elseif _isUnderwearOnly>>
<<if $corruption >= 4>>
<<set _canExit to true>>
<<else>>
<<set _reason to "You can't walk out in just your underwear. (Corruption 4 required)">>
<</if>>
<<elseif _isNaked>>
<<if $corruption >= 5 and $exhibitionism >= 10>>
<<set _canExit to true>>
<<elseif $corruption < 5>>
<<set _reason to "You can't walk out naked. (Corruption 5 + Exhibitionism 10 required)">>
<<else>>
<<set _reason to "You need more exhibitionism to walk out naked. (Exhibitionism 10 required)">>
<</if>>
<</if>>
/* Navigate */
<<if _canExit>>
<<advanceTime 1>>
<<goto "fhUpperstairs">>
<<else>>
<<notify "warning" _reason>>
<<goto "fhBedroom">>
<</if>>
<</widget>>
/*
WIDGET: outfitGateLockCheck
Evaluates whether the player's current outfit is allowed for the given exit context.
Args[0] : context string "indoor" or "outdoor" (matches keys in setup.outfitExitRules)
Output : Sets _outfitLockMsg to "" (allowed) or a reason string (blocked).
Callers pass _outfitLockMsg as args[3] to <<navCard>> to lock the card.
*/
<<widget "outfitGateLockCheck">>
/* Safeguards */
<<if !$wardrobe>><<set $wardrobe = { equipped: {} }>><</if>>
<<if !$wardrobe.equipped>><<set $wardrobe.equipped = {}>><</if>>
<<if ndef $corruption>><<set $corruption = 0>><</if>>
<<if ndef $exhibitionism>><<set $exhibitionism = 0>><</if>>
/* Read rules for the requested context */
<<set _ogCtx = (_args[0] || "indoor")>>
<<set _ogRules = (setup.outfitExitRules && setup.outfitExitRules[_ogCtx]) ? setup.outfitExitRules[_ogCtx] : {}>>
/* Always initialise outputs first */
<<set _outfitLockMsg = "">>
<<set _outfitConfirmMsg = "">>
/* Shoes check blocks all other evaluation if failed */
<<set _ogHasShoes = ($wardrobe.equipped.shoes && $wardrobe.equipped.shoes !== "")>>
<<if _ogRules.shoesRequired && !_ogHasShoes>>
<<set _outfitLockMsg = "You need to put on shoes before going outside.">>
<<return>>
<</if>>
/* Coverage flags */
<<set _ogHasBodysuit = ($wardrobe.equipped.bodysuit && $wardrobe.equipped.bodysuit !== "")>>
<<set _ogHasTop = (($wardrobe.equipped.top && $wardrobe.equipped.top !== "") || _ogHasBodysuit)>>
<<set _ogHasBottom = ($wardrobe.equipped.bottom && $wardrobe.equipped.bottom !== "")>>
<<set _ogHasDress = ($wardrobe.equipped.dress && $wardrobe.equipped.dress !== "")>>
<<set _ogHasBra = (($wardrobe.equipped.bra && $wardrobe.equipped.bra !== "") || _ogHasBodysuit)>>
<<set _ogHasPanty = ($wardrobe.equipped.panty && $wardrobe.equipped.panty !== "")>>
<<set _ogHasSleepwear = ($wardrobe.equipped.sleepwear && $wardrobe.equipped.sleepwear !== "")>>
/* Outfit categories */
<<set _ogFullyDressed = (_ogHasDress || ((_ogHasTop || _ogHasBodysuit) && _ogHasBottom))>>
<<set _ogInSleepwear = (_ogHasSleepwear && !_ogFullyDressed)>>
<<set _ogUnderwearOnly = (!_ogFullyDressed && !_ogHasSleepwear && ((_ogHasBra || _ogHasPanty) || _ogHasBodysuit))>>
<<set _ogNaked = (!_ogFullyDressed && !_ogHasSleepwear && !_ogHasBra && !_ogHasPanty && !_ogHasBodysuit)>>
/* Sleepwear sexiness */
<<set _ogSwSexiness = 0>>
<<if _ogInSleepwear>>
<<set _ogSwItem = setup.getClothingById($wardrobe.equipped.sleepwear)>>
<<if _ogSwItem>><<set _ogSwSexiness = (_ogSwItem.sexinessScore || 0)>><</if>>
<</if>>
/* Evaluate */
<<if _ogFullyDressed>>
/* Fully dressed always free, no confirmation needed */
<<elseif _ogInSleepwear>>
<<set _ogSexyThreshold = (_ogRules.sleepwearSexinessThreshold || 3)>>
<<if _ogSwSexiness >= _ogSexyThreshold>>
<<set _ogReq = (_ogRules.sleepwearSexyMinCorr || 3)>>
<<if $corruption < _ogReq>>
<<set _outfitLockMsg = "Sexy sleepwear Corruption " + _ogReq + " required.">>
<<else>>
<<set _outfitConfirmMsg = "You're wearing revealing sleepwear. Are you sure you want to leave like this?">>
<</if>>
<<else>>
<<if _ogCtx is "outdoor">>
<<set _ogReq = (_ogRules.sleepwearNormalMinCorr || 2)>>
<<if $corruption < _ogReq>>
<<set _outfitLockMsg = "Going outside in sleepwear requires Corruption " + _ogReq + ".">>
<<else>>
<<set _outfitConfirmMsg = "You're still in your sleepwear. Are you sure you want to go outside like this?">>
<</if>>
<</if>>
/* Non-sexy sleepwear indoors completely free, no confirmation needed */
<</if>>
<<elseif _ogUnderwearOnly>>
<<set _ogReqCorr = (_ogRules.underwearMinCorr || 4)>>
<<set _ogReqExh = (_ogRules.underwearMinExh || 0)>>
<<if $corruption < _ogReqCorr>>
<<set _outfitLockMsg = "Underwear only Corruption " + _ogReqCorr + " required.">>
<<elseif _ogReqExh > 0 && $exhibitionism < _ogReqExh>>
<<set _outfitLockMsg = "Underwear only Exhibitionism " + _ogReqExh + " required.">>
<<else>>
<<set _outfitConfirmMsg = "You're only wearing underwear. Are you sure you want to leave like this?">>
<</if>>
<<elseif _ogNaked>>
<<set _ogReqCorr = (_ogRules.nakedMinCorr || 5)>>
<<set _ogReqExh = (_ogRules.nakedMinExh || 10)>>
<<if $corruption < _ogReqCorr || $exhibitionism < _ogReqExh>>
<<set _outfitLockMsg = "Naked Corruption " + _ogReqCorr + " and Exhibitionism " + _ogReqExh + " required.">>
<<else>>
<<set _outfitConfirmMsg = "You're completely naked. Are you sure you want to leave like this?">>
<</if>>
<</if>>
<</widget>>
/*
WIDGET: updateClothesNotification
Sets $notificationClothes based on current wardrobe state
Values:
0 = Normal (no notification)
1 = Naked
2 = Underwear Only
3 = No Underwear (Commando)
4 = Revealing Outfit
*/
<<widget "updateClothesNotification">>
/* Variable Safeguards */
<<if !$wardrobe>><<set $wardrobe = { equipped: {} }>><</if>>
<<if !$wardrobe.equipped>><<set $wardrobe.equipped = {} >><</if>>
/* Calculate Coverage */
<<set _hasBodysuit to ($wardrobe.equipped.bodysuit and $wardrobe.equipped.bodysuit !== "")>>
<<set _hasTop to (($wardrobe.equipped.top and $wardrobe.equipped.top !== "") or _hasBodysuit)>>
<<set _hasBottom to ($wardrobe.equipped.bottom and $wardrobe.equipped.bottom !== "")>>
<<set _hasDress to ($wardrobe.equipped.dress and $wardrobe.equipped.dress !== "")>>
<<set _hasBra to (($wardrobe.equipped.bra and $wardrobe.equipped.bra !== "") or _hasBodysuit)>>
<<set _hasPanty to ($wardrobe.equipped.panty and $wardrobe.equipped.panty !== "")>>
/* Determine Outfit State */
<<set _isFullyDressed to (_hasDress or ((_hasTop or _hasBodysuit) and _hasBottom))>>
<<set _hasAnyUnderwear to (_hasBra or _hasPanty)>>
<<set _hasFullUnderwear to (_hasBra and _hasPanty)>>
<<set _isUnderwearOnly to (!_isFullyDressed and _hasAnyUnderwear)>>
<<set _isNude to (!_isFullyDressed and !_hasAnyUnderwear and !_hasBodysuit)>>
<<set _isCommando to (_isFullyDressed and !_hasPanty and !_hasDress and (_hasTop or _hasBodysuit))>>
/* Check for Revealing Tags */
<<set _isRevealing to false>>
<<if _isFullyDressed>>
<<if _hasDress>>
<<set _dressItem to setup.getClothingById($wardrobe.equipped.dress)>>
<<if _dressItem and _dressItem.tags>>
<<if _dressItem.tags.includes("revealing") or _dressItem.tags.includes("daring") or _dressItem.tags.includes("erotic") or _dressItem.tags.includes("lewd")>>
<<set _isRevealing to true>>
<</if>>
<</if>>
<<else>>
<<if _hasBodysuit>>
<<set _topItem to setup.getClothingById($wardrobe.equipped.bodysuit)>>
<<else>>
<<set _topItem to setup.getClothingById($wardrobe.equipped.top)>>
<</if>>
<<set _bottomItem to setup.getClothingById($wardrobe.equipped.bottom)>>
<<if _topItem and _topItem.tags>>
<<if _topItem.tags.includes("revealing") or _topItem.tags.includes("daring") or _topItem.tags.includes("erotic") or _topItem.tags.includes("lewd")>>
<<set _isRevealing to true>>
<</if>>
<</if>>
<<if _bottomItem and _bottomItem.tags>>
<<if _bottomItem.tags.includes("revealing") or _bottomItem.tags.includes("daring") or _bottomItem.tags.includes("erotic") or _bottomItem.tags.includes("lewd")>>
<<set _isRevealing to true>>
<</if>>
<</if>>
<</if>>
<</if>>
/* Set Notification Value - Priority Order */
<<if _isNude>>
<<set $notificationClothes to 1>>
<<elseif _isUnderwearOnly>>
<<set $notificationClothes to 2>>
<<elseif _isCommando>>
<<set $notificationClothes to 3>>
<<elseif _isRevealing>>
<<set $notificationClothes to 4>>
<<else>>
<<set $notificationClothes to 0>>
<</if>>
<</widget>><<silently>>
<<set _sceneIndex = random(1, 2)>>
<<if ndef $returnAfterToilet>>
<<set $returnAfterToilet = "fhUpperBath">>
<</if>>
<<if ndef $makeup>><<set $makeup = { state: "off", style: 0, quality: 0, lastDecayHourKey: "", lastAppliedHourKey: "", lastDecayMinuteStamp: 0 }>><</if>>
<<set $makeup.state = "off">>
<<set $makeup.style = 0>>
<<set $makeup.quality = 0>>
<<set $makeup.lastDecayHourKey = "">>
<<set $makeup.lastAppliedHourKey = "">>
<<set $makeup.lastDecayMinuteStamp = 0>>
<<set $location = $returnAfterToilet>>
<<advanceTime 5>>
<<gainStat "hygiene" 10>>
<<gainStat "energy" 5>>
<<flushNotifications>>
<<recalculateStats>>
<</silently>>
<<narrative "Wash Face">>
You splash cold water on your face, scrubbing gently. It wakes you up a little and makes you feel cleaner.
<</narrative>>
<<if _sceneIndex === 1>>
<<vid "assets/content/scenes/global/bathroom/washface/washface1.mp4" "60%">>
<<else>>
<<vid "assets/content/scenes/global/bathroom/washface/washface2.mp4" "100%">>
<</if>>
<div class="location-actions">
<<btn "Done" $returnAfterToilet>><</btn>>
</div><<nobr>>
<<silently>>
<<set $location = "fhLivingroom">>
<<set _tvMins = parseInt($selectedDuration) || 15>>
<<set _tvScale = _tvMins / 30>>
/* Energy: ~5/hr passive cap base 2 first 15m, +1 per extra 15m, max 6; advanceTime uses "relax" to skip hourly passive energy drain */
<<set _energyCost = 2>>
<<if _tvMins > 15>>
<<set _energyCost = 2 + Math.floor((_tvMins - 15) / 15) * 1>>
<<set _energyCost = Math.min(6, _energyCost)>>
<</if>>
<<set _tvNeedEnergy = _energyCost + 10>>
<<requireMinEnergy _tvNeedEnergy "fhCouch" "Not enough energy.">>
<<advanceTime _tvMins "relax">>
<<set _tvStressLoss = Math.max(1, Math.round(7 * _tvScale))>>
<<set _tvMoodGain = Math.max(1, Math.round(10 * _tvScale))>>
<<set _stressBefore = $stress || 0>>
<<set _moodBefore = $mood || 0>>
<<set $stress = Math.max(0, _stressBefore - _tvStressLoss)>>
<<set _actualStressLoss = _stressBefore - $stress>>
<<if _actualStressLoss > 0>><<queueStatChange "stress" (-1 * _actualStressLoss)>><</if>>
<<set $mood = Math.min(100, _moodBefore + _tvMoodGain)>>
<<set _actualMoodGain = $mood - _moodBefore>>
<<if _actualMoodGain > 0>><<queueStatChange "mood" _actualMoodGain>><</if>>
<<run window.notifySuccess("Stress Relieved")>>
<<set _energyBefore = $energy || 0>>
<<set $energy = Math.max(0, _energyBefore - _energyCost)>>
<<set _actualEnergyLoss = _energyBefore - $energy>>
<<if _actualEnergyLoss > 0>><<queueStatChange "energy" (-1 * _actualEnergyLoss)>><</if>>
<<recalculateStats>>
<<set _watchingWith = $watchingWith || "alone">>
<<set _useSportsTopic = false>>
/* Get current time period */
<<getPeriod>>
<<set _timePeriod = _period>>
/* For image paths: morning/afternoon use "morning", evening/night use "evening" */
<<if _timePeriod === "morning" || _timePeriod === "afternoon">>
<<set _imgFolder = "morning">>
<<else>>
<<set _imgFolder = "evening">>
<</if>>
/* ============================================
TEXT & IMAGE VARIATIONS BY CHARACTER
Each variation has matching text + image
============================================ */
/* ----- ALONE ----- */
<<if _watchingWith === "alone">>
<<if _imgFolder === "morning">>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "You settle onto the couch and flip through channels until you land on some sitcom rerun. A party scene plays out on screen, people laughing and mingling. It's mindless entertainment, perfect for a lazy morning.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/alone/aloneMorning1.webp">>
<<case 2>>
<<set _sceneText = "You curl up on the couch with nothing but the TV for company. Some feel-good movie is on - friends chatting at a cafe, sharing coffee and stories. The warm atmosphere on screen makes the empty room feel a little less quiet.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/alone/aloneMorning2.webp">>
<</switch>>
<<else>>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "The living room is dark except for the soft glow of the TV. You've stumbled upon a nature documentary - deer grazing peacefully in golden grasslands. It's oddly calming, watching wildlife while the rest of the house sleeps.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/alone/aloneNight1.webp">>
<<case 2>>
<<set _sceneText = "Late night TV hits different when you're alone. Some romantic drama is playing - two people sharing an intense look that promises complications. You sink deeper into the couch, letting the story wash over you.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/alone/aloneNight2.webp">>
<</switch>>
<</if>>
/* ----- MOM ----- */
<<elseif _watchingWith === "mom">>
<<gainStat "friendship" 2 "mother">>
<<if _imgFolder === "morning">>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "Mom's already deep into her morning drama when you join her. On screen, a couple shares an intense moment. She sighs dreamily and pats the seat next to her, eager to have someone to discuss the plot twists with.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/mom1.webp">>
<<case 2>>
<<set _sceneText = "You find Mom watching her favorite hospital drama. Doctors rush through corridors on screen while she leans forward, completely absorbed. 'Sit, sit!' she whispers urgently. 'You won't believe what just happened.'">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/mom2.webp">>
<</switch>>
<<else>>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "Mom pats the seat beside her as you enter the room. Her favorite romantic drama is on - a couple locked in one of those will-they-won't-they moments. She's been following this show for weeks and gives you a whispered recap.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/mom1.webp">>
<<case 2>>
<<set _sceneText = "You find Mom watching some period drama - elaborate costumes, forbidden glances across ballrooms. She's completely enchanted by it. 'The cinematography is gorgeous,' she murmurs, making room for you on the couch.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/mom2.webp">>
<</switch>>
<</if>>
/* ----- DAD ----- (Watch Sports uses setup.fatherTopics living room pools, sports/tv first) */
<<elseif _watchingWith === "dad">>
<<set _useSportsTopic = true>>
<<set _level = $characters.father.stats.friendshipLevel || 1>>
<<set _phase = (def $flags && $flags.fatherStartedWork) ? "postWork" : "preWork">>
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _contextKey = "fhLivingroom_" + _phase + "_" + _timeSlot>>
<<set _phasePool = setup.fatherTopics[_phase] && setup.fatherTopics[_phase]["level" + _level]>>
<<set _context = (_phasePool && _phasePool[_contextKey]) ? _phasePool[_contextKey] : null>>
<<if !_context && _phasePool>>
<<set _context = _phasePool["fhLivingroom_" + _phase + "_evening"]>>
<<if !_context>>
<<set _context = _phasePool["fhLivingroom_" + _phase + "_morning"]>>
<</if>>
<</if>>
<<if _context && _context.sports && _context.sports.length>>
<<set _topic = _context.sports.random()>>
<<elseif _context && _context.tv && _context.tv.length>>
<<set _topic = _context.tv.random()>>
<<elseif _context>>
<<set _topicKeys = Object.keys(_context)>>
<<if _topicKeys.length>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
<</if>>
<</if>>
<<if !_topic>>
<<set _topic = { text: "You watch whatever's on together mostly the game, some sideline commentary, easy quiet between plays.", friendship: 1 }>>
<</if>>
<<if _topic.friendship>><<gainCharacterStat "father" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "father" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "father" "love" _topic.love>><</if>>
<<recalculateStats>>
<<if _imgFolder === "morning">>
<<set _sportsIntro = "The big game is on, and your father has claimed his spot on the couch. He looks happythis is his element.">>
<<else>>
<<set _sportsIntro = "After a long day at work, your father finally gets to unwind with the game. It's one of his few escapes.">>
<</if>>
/* ----- BROTHER ----- */
<<elseif _watchingWith === "brother">>
<<gainStat "friendship" 2 "brother">>
<<if _imgFolder === "morning">>
<<set _maxVariation = 4>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "Your brother has put on some romantic movie. A couple is sharing a kiss on screen. He catches your look and shrugs defensively. 'What? It was already on.' You both pretend to be very interested in the plot.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/brother1.webp">>
<<case 2>>
<<set _sceneText = "You plop down next to your brother. Some sitcom rerun is on - a group of friends hanging out in their usual spot. He's seen this episode a hundred times but still laughs at all the jokes.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/brother2.webp">>
<<case 3>>
<<set _sceneText = "Another romantic film is playing. Your brother is pretending not to pay attention, scrolling through his phone, but you notice he looks up at all the important moments. Neither of you comments on it.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/brother3.webp">>
<<case 4>>
<<set _sceneText = "You sit down next to your brother just as things on screen start heating up. Neither of you reaches for the remote. You both stare straight ahead, pretending to be very mature about the whole situation. The silence is deafening.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/brother4.webp">>
<</switch>>
<<else>>
<<set _maxVariation = 3>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "You and your brother sprawl across the couch. Some sitcom is on - a group of friends getting into ridiculous situations. You both quote the punchlines before they happen, cracking up every time.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/brother1.webp">>
<<case 2>>
<<set _sceneText = "A romantic drama is playing, and your brother keeps making sarcastic comments about the characters' life choices. You join in, and soon you're both providing your own commentary track for the entire film.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/brother2.webp">>
<<case 3>>
<<set _sceneText = "Some intense drama is on screen - serious conversations, meaningful looks. Your brother watches quietly for once, actually invested in the story. You settle in beside him, drawn into the plot despite yourself.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/brother3.webp">>
<</switch>>
<</if>>
/* ----- MOM & BROTHER ----- */
<<elseif _watchingWith === "mombrother">>
<<gainStat "friendship" 1 "mother">>
<<gainStat "friendship" 1 "brother">>
<<if _imgFolder === "morning">>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "Mom's already deep into her morning drama when you and your brother join her. On screen, a couple shares an intense moment. She sighs dreamily and pats the seat next to her, eager to have someone to discuss the plot twists with.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/mom1.webp">>
<<case 2>>
<<set _sceneText = "You find Mom and your brother watching her favorite hospital drama. Doctors rush through corridors on screen while she leans forward, completely absorbed. 'Sit, sit!' she whispers urgently. 'You won't believe what just happened.'">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/mom2.webp">>
<</switch>>
<<else>>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "Mom pats the seat beside her as you enter the room. Your brother's already there. Her favorite romantic drama is on - a couple locked in one of those will-they-won't-they moments. She's been following this show for weeks and gives you a whispered recap.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/mom1.webp">>
<<case 2>>
<<set _sceneText = "You find Mom and your brother watching some period drama - elaborate costumes, forbidden glances across ballrooms. She's completely enchanted by it. 'The cinematography is gorgeous,' she murmurs, making room for you on the couch.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/mom2.webp">>
<</switch>>
<</if>>
/* ----- DAD & BROTHER ----- */
<<elseif _watchingWith === "dadbrother">>
<<set _useSportsTopic = true>>
<<set _level = $characters.father.stats.friendshipLevel || 1>>
<<set _phase = (def $flags && $flags.fatherStartedWork) ? "postWork" : "preWork">>
<<set _hour = $timeSys.hour>>
<<if _hour >= 6 && _hour < 12>>
<<set _timeSlot = "morning">>
<<elseif _hour >= 12 && _hour < 18>>
<<set _timeSlot = "afternoon">>
<<else>>
<<set _timeSlot = "evening">>
<</if>>
<<set _contextKey = "fhLivingroom_" + _phase + "_" + _timeSlot>>
<<set _phasePool = setup.fatherTopics[_phase] && setup.fatherTopics[_phase]["level" + _level]>>
<<set _context = (_phasePool && _phasePool[_contextKey]) ? _phasePool[_contextKey] : null>>
<<if !_context && _phasePool>>
<<set _context = _phasePool["fhLivingroom_" + _phase + "_evening"]>>
<<if !_context>>
<<set _context = _phasePool["fhLivingroom_" + _phase + "_morning"]>>
<</if>>
<</if>>
<<if _context && _context.sports && _context.sports.length>>
<<set _topic = _context.sports.random()>>
<<elseif _context && _context.tv && _context.tv.length>>
<<set _topic = _context.tv.random()>>
<<elseif _context>>
<<set _topicKeys = Object.keys(_context)>>
<<if _topicKeys.length>>
<<set _topicKey = _topicKeys.random()>>
<<set _topic = _context[_topicKey].random()>>
<</if>>
<</if>>
<<if !_topic>>
<<set _topic = { text: "You watch whatever's on together mostly the game, some sideline commentary, easy quiet between plays.", friendship: 1 }>>
<</if>>
<<if _topic.friendship>><<gainCharacterStat "father" "friendship" _topic.friendship>><</if>>
<<if _topic.trust>><<gainCharacterStat "father" "trust" _topic.trust>><</if>>
<<if _topic.love>><<gainCharacterStat "father" "love" _topic.love>><</if>>
<<gainCharacterStat "brother" "friendship" 1>>
<<recalculateStats>>
<<if _imgFolder === "morning">>
<<set _sportsIntro = "The big game is on, and your father has claimed his spot on the couch. Your brother's there too. He looks happythis is his element.">>
<<else>>
<<set _sportsIntro = "After a long day at work, your father finally gets to unwind with the game. Your brother's beside him. It's one of his few escapes.">>
<</if>>
/* ----- MOM & DAD ----- */
<<elseif _watchingWith === "momdad">>
<<gainStat "friendship" 1 "mother">>
<<gainStat "friendship" 1 "father">>
<<if _imgFolder === "morning">>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "Your parents are watching some home improvement show together. On screen, a family gathers in a newly renovated kitchen. Mom keeps pointing out design ideas while Dad calculates costs in his head. You squeeze in beside them.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/momdad1.webp">>
<<case 2>>
<<set _sceneText = "Mom and Dad are absorbed in a crime drama - someone's being interrogated on screen. They keep whispering theories to each other about who the real culprit is. You join them, adding your own wild guesses to the mix.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/momdad2.webp">>
<</switch>>
<<else>>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "Both your parents are on the couch, Mom leaning against Dad. They're watching some romantic drama they've probably seen before. You curl up beside them, enjoying the familiar comfort of the moment.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/momdad1.webp">>
<<case 2>>
<<set _sceneText = "A cooking competition is on, and your parents are fully invested. Mom critiques the plating while Dad questions the time management. You settle in to watch the culinary chaos unfold together.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/momdad2.webp">>
<</switch>>
<</if>>
/* ----- WHOLE FAMILY ----- */
<<elseif _watchingWith === "family">>
<<gainStat "friendship" 1 "mother">>
<<gainStat "friendship" 1 "father">>
<<gainStat "friendship" 1 "brother">>
<<if _imgFolder === "morning">>
<<set _maxVariation = 2>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "A rare lazy morning with everyone home. The whole family is gathered around the TV watching some drama. Mom's already emotional about the couple on screen, while your brother rolls his eyes and Dad pretends not to be interested.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/family1.webp">>
<<case 2>>
<<set _sceneText = "Weekend TV with the whole family. Some workplace drama is on - people arguing in a conference room. Dad keeps comparing it to his office, Mom says it's unrealistic, and your brother is just here for the snacks.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/morning/family2.webp">>
<</switch>>
<<else>>
<<set _maxVariation = 3>>
<<set _variation = random(1, _maxVariation)>>
<<switch _variation>>
<<case 1>>
<<set _sceneText = "Family night in front of a reality show. Everyone has opinions about the contestants - Mom picks favorites, Dad critiques strategy, your brother mocks the drama. It's chaos, but it's your kind of chaos.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/family1.webp">>
<<case 2>>
<<set _sceneText = "Someone put on a cartoon movie and no one's complaining. Dad's arm is around Mom, your brother has claimed the good corner, and colorful characters bounce across the screen. Simple, wholesome family time.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/family2.webp">>
<<case 3>>
<<set _sceneText = "A talent show is on, and the whole family is playing judge. Mom's too kind with her scores, Dad's too harsh, and you and your brother argue over who should have won. The actual winners are secondary to your family debate.">>
<<set _sceneImage = "assets/content/scenes/maplewood/FamilyHouse/livingroom/watchTv/evening/family3.webp">>
<</switch>>
<</if>>
<</if>>
<<flushNotifications>>
<</silently>>
/* ============================================
SCENE DISPLAY
============================================ */
<<if _useSportsTopic>>
<<narrative "Living Room - Watch Sports with Father">>
_sportsIntro
<</narrative>>
<<print _topic.text>>
<<else>>
<<narrative "Watching TV">>
_sceneText
<</narrative>>
<<image _sceneImage "100%">>
<</if>>
<div class="location-actions">
<<btn "Turn Off TV" "fhCouch">><</btn>>
</div>
<</nobr>><<silently>>
/* Evaluate the pick */
<<if $gymMG.timedOut>>
<<set $gymMG.lastResult = "timeout">>
<<elseif $gymMG.picked === $gymMG.zone>>
<<set $gymMG.lastResult = "correct">>
<<set $gymMG.score += 1>>
<<else>>
<<set $gymMG.lastResult = "wrong">>
<</if>>
/* Advance to next phase */
<<set $gymMG.phase += 1>>
<</silently>>
<div class="gym-mg-feedback-screen gym-mg-feedback-<<print $gymMG.lastResult>>">
<<if $gymMG.lastResult === "correct">>
<span class="gym-mg-feedback-msg gym-mg-feedback-correct">Good focus!</span>
<<elseif $gymMG.lastResult === "timeout">>
<span class="gym-mg-feedback-msg gym-mg-feedback-timeout">Too slow!</span>
<<else>>
<span class="gym-mg-feedback-msg gym-mg-feedback-wrong">Wrong zone!</span>
<</if>>
</div>
<<script>>
(function () {
var mg = State.variables.gymMG;
if (!mg) {
Engine.play('gym');
return;
}
var dest = (mg.phase > mg.totalPhases) ? 'gymMiniGameResult' : 'gymMiniGameRound';
if (window._gymMGChoiceTimeout) {
clearTimeout(window._gymMGChoiceTimeout);
window._gymMGChoiceTimeout = null;
}
window._gymMGChoiceTimeout = setTimeout(function () {
window._gymMGChoiceTimeout = null;
Engine.play(dest);
}, 700);
})();
<</script>>
<<set $location = "gym">>
<div class="gym-tutorial-trigger">Preparing workout...</div>
<div id="gymTutorialFallback" class="gym-tutorial-fallback">
<<narrative "Workout Briefing">>
Tutorial modal couldn't be opened. Start training directly.
<</narrative>>
<div class="location-actions">
<<btn "Start Training" "gymMiniGameRound">>
<<set $flags.gymMiniGameTutorialSeen = true>>
<</btn>>
<<btn "Back to Gym" "gym">><</btn>>
</div>
</div>
<<script>>
(function () {
if (window.openGymTutorialModal && window.openGymTutorialModal()) {
var fallback = document.getElementById('gymTutorialFallback');
if (fallback) fallback.style.display = 'none';
} else {
State.variables.flags = State.variables.flags || {};
if (window.console && console.warn) {
console.warn('gym tutorial modal failed, using fallback buttons.');
}
}
})();
<</script>>
<<nobr>>
<<silently>>
<<set $location = "gym">>
<<set _score = $gymMG.score>>
<<set _zone = $gymMG.zone>>
<<set _gymVidDefs = {
upper: { folder: "upper", prefix: "upperbody", max: 13, size60: [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12] },
core: { folder: "core", prefix: "core", max: 6, size60: [1, 3] },
lower: { folder: "lower", prefix: "lowerbody", max: 13, size60: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] },
cardio: { folder: "cardio", prefix: "cardio", max: 8, size60: [1, 2, 6, 7, 8] }
}>>
<<set _gymVidDef = _gymVidDefs[_zone] || _gymVidDefs.upper>>
<<set _gymVidVariant = random(1, _gymVidDef.max)>>
<<set _gymVid = "assets/content/scenes/global/gym/" + _gymVidDef.folder + "/" + _gymVidDef.prefix + _gymVidVariant + ".mp4">>
<<set _gymVidSize = "100%">>
<<if _gymVidDef.size60.includes(_gymVidVariant)>>
<<set _gymVidSize = "60%">>
<</if>>
<<set _completedPhases = Math.min($gymMG.totalPhases, Math.max(0, $gymMG.phase - 1))>>
<<set _correctHits = Math.min($gymMG.totalPhases, Math.max(0, Math.floor(Number(_score) || 0)))>>
/* ── Per-phase stat reward (skill logic ported to stats) ── */
<<set _mainPerPhase = 2 / $gymMG.totalPhases>>
<<set _supPerPhase = _mainPerPhase / 2>>
<<set _statMain = _score * _mainPerPhase>>
<<set _statSup = _score * _supPerPhase>>
<<set _scoreRatio = (_score / $gymMG.totalPhases)>>
<<if _scoreRatio >= 0.75>>
<<set _tier = "great">>
<<elseif _scoreRatio >= 0.25>>
<<set _tier = "decent">>
<<else>>
<<set _tier = "rough">>
<</if>>
/* ── Zone → stat mapping ── */
<<if _zone === "upper">>
<<gainStat "upperBody" _statMain>>
<<if _statSup > 0>><<gainStat "core" _statSup>><</if>>
<<set _statMainName = "Upper Body">>
<<set _statSupName = "Core">>
<<elseif _zone === "core">>
<<gainStat "core" _statMain>>
<<if _statSup > 0>><<gainStat "lowerBody" _statSup>><</if>>
<<set _statMainName = "Core">>
<<set _statSupName = "Lower Body">>
<<elseif _zone === "lower">>
<<gainStat "lowerBody" _statMain>>
<<if _statSup > 0>><<gainStat "cardio" _statSup>><</if>>
<<set _statMainName = "Lower Body">>
<<set _statSupName = "Cardio">>
<<else>>
<<gainStat "cardio" _statMain>>
<<if _statSup > 0>><<gainStat "lowerBody" _statSup>><</if>>
<<set _statMainName = "Cardio">>
<<set _statSupName = "Lower Body">>
<</if>>
<<recalculateStats>>
<<flushNotifications>>
/* ── Display labels ── */
<<set _zoneLabels = { upper: "Upper Body", core: "Core", lower: "Lower Body", cardio: "Cardio" }>>
<<set _tierLabels = { great: "Great Session", decent: "Decent Session", rough: "Rough Session" }>>
<</silently>>
<div class="gym-mg-result-wrap">
<!-- Session badge -->
<div class="gym-mg-result-header">
<span class="gym-mg-result-tier gym-mg-tier-<<print _tier>>"><<print _tierLabels[_tier]>></span>
<div class="gym-mg-result-title">Training Complete</div>
<div class="gym-mg-result-subtitle"><<print _zoneLabels[_zone]>></div>
</div>
</div>
<<vid _gymVid _gymVidSize>>
<div class="gym-mg-result-wrap">
<!-- Phase bar -->
<div class="gym-mg-result-block">
<div class="gym-mg-result-row">
<span class="gym-mg-result-row-label">Phases Completed</span>
<span class="gym-mg-result-row-val"><<print _completedPhases>> <span class="gym-mg-result-row-total">/ <<print $gymMG.totalPhases>></span></span>
</div>
<div class="gym-mg-phase-bar" style="display:flex;gap:6px;margin-top:10px;width:100%;">
<<for _pi = 1; _pi <= $gymMG.totalPhases; _pi++>>
<<if _pi <= _correctHits>><div class="gym-mg-phase-seg gym-mg-phase-seg-done" style="flex:1 1 0;height:10px;border-radius:4px;background:#ec4899;"> </div><<else>><div class="gym-mg-phase-seg" style="flex:1 1 0;height:10px;border-radius:4px;background:#3a3a44;"> </div><</if>>
<</for>>
</div>
</div>
<!-- Rewards -->
<div class="gym-mg-result-block">
<div class="gym-mg-result-block-label">Gained</div>
<div class="gym-mg-result-row">
<span class="gym-mg-result-row-label"><<print _statMainName>></span>
<span class="gym-mg-result-reward-stat">+<<print _statMain.toFixed(1)>></span>
</div>
<<if _statSup > 0>>
<div class="gym-mg-result-row">
<span class="gym-mg-result-row-label"><<print _statSupName>></span>
<span class="gym-mg-result-reward-sup">+<<print _statSup.toFixed(2)>></span>
</div>
<</if>>
</div>
</div>
<div class="location-actions">
<<btn "Return to Gym" "gym">><</btn>>
</div>
<</nobr>><<nobr>>
<<set $location = "gym">>
<<silently>>
<<if ndef $gymMG.feedbackMode>><<set $gymMG.feedbackMode = false>><</if>>
<<if !$gymMG.feedbackMode>>
/* Build 5 choices: 1 correct + 4 wrong (3 other zones + repeat first wrong) */
<<set _allZones = ["upper", "core", "lower", "cardio"]>>
<<set _wrongZones = []>>
<<for _zi = 0; _zi < _allZones.length; _zi++>>
<<if _allZones[_zi] !== $gymMG.zone>>
<<run _wrongZones.push(_allZones[_zi])>>
<</if>>
<</for>>
/* _wrongZones has 3 entries; add the first one again to get 4 wrong */
<<set _pool = [_wrongZones[0], _wrongZones[1], _wrongZones[2], _wrongZones[0], $gymMG.zone]>>
<<set $gymMG.choices = _pool.sort(function() { return Math.random() - 0.5; })>>
<<set $gymMG.timedOut = false>>
<<set $gymMG.picked = null>>
<<set $gymMG.lastResult = "">>
<<set $gymMG.locked = false>>
<</if>>
/* Display helpers */
<<set _zoneLabels = { upper: "Upper Body", core: "Core", lower: "Lower Body", cardio: "Cardio" }>>
<<set _zoneIcons = { upper: "upperbody", core: "core", lower: "lowerbody", cardio: "cardio" }>>
<<set _zoneLabel = _zoneLabels[$gymMG.zone]>>
<<set _zoneStatMain = { upper: "Upper Body", core: "Core", lower: "Lower Body", cardio: "Cardio" }>>
<<set _zoneStatSup = { upper: "Core", core: "Lower Body", lower: "Cardio", cardio: "Lower Body" }>>
<<set _statMainName = _zoneStatMain[$gymMG.zone]>>
<<set _statSupName = _zoneStatSup[$gymMG.zone]>>
/* Per-phase stat rewards: main max +2.0, support scales proportionally */
<<set _mainPerPhase = 2 / $gymMG.totalPhases>>
<<set _supPerPhase = _mainPerPhase / 2>>
<<set _previewMain = $gymMG.score * _mainPerPhase>>
<<set _previewSup = $gymMG.score * _supPerPhase>>
<</silently>>
<div class="gym-mg-wrap">
<!-- Header -->
<div class="gym-mg-header">
<div>
<div class="gym-mg-zone-name"><<print _zoneLabel>> Training</div>
<div class="gym-mg-phase-count">Phase <<print $gymMG.phase>> <span class="gym-mg-phase-total">/ <<print $gymMG.totalPhases>></span></div>
</div>
<div class="gym-mg-score-block">
<div class="gym-mg-score-label">Stat Gain</div>
<div class="gym-mg-score-val"><<print _statMainName>> +<<print _previewMain.toFixed(1)>></div>
<<if _previewSup > 0>>
<div class="gym-mg-score-sub"><<print _statSupName>> +<<print _previewSup.toFixed(2)>></div>
<</if>>
</div>
</div>
<!-- Timer -->
<div class="gym-mg-timer-wrap">
<div class="gym-mg-timer-row">
<span class="gym-mg-timer-label">Timer</span>
<span id="gymMGTimerText" class="gym-mg-timer-text">5s</span>
</div>
<div class="gym-mg-timer-track">
<div id="gymMGTimerBar" class="gym-mg-timer-fill"></div>
</div>
</div>
<<if $gymMG.feedbackMode>>
<div class="gym-mg-feedback-slot">
<div class="gym-mg-feedback-screen gym-mg-feedback-<<print $gymMG.lastResult>>">
<<if $gymMG.lastResult === "correct">>
<span class="gym-mg-feedback-msg gym-mg-feedback-correct">Perfect Zone</span>
<<elseif $gymMG.lastResult === "timeout">>
<span class="gym-mg-feedback-msg gym-mg-feedback-timeout">Too Slow</span>
<<else>>
<span class="gym-mg-feedback-msg gym-mg-feedback-wrong">Wrong Zone</span>
<</if>>
</div>
</div>
<<else>>
<!-- Zone choice buttons: exactly 5 btn macros -->
<div class="gym-mg-choices">
<<set _btn0 = "<i class='icon icon-" + _zoneIcons[$gymMG.choices[0]] + " icon-32'></i>">>
<<set _btn1 = "<i class='icon icon-" + _zoneIcons[$gymMG.choices[1]] + " icon-32'></i>">>
<<set _btn2 = "<i class='icon icon-" + _zoneIcons[$gymMG.choices[2]] + " icon-32'></i>">>
<<set _btn3 = "<i class='icon icon-" + _zoneIcons[$gymMG.choices[3]] + " icon-32'></i>">>
<<set _btn4 = "<i class='icon icon-" + _zoneIcons[$gymMG.choices[4]] + " icon-32'></i>">>
<<btn _btn0 "gymMiniGameRound" "gym-zone-btn gym-zone-btn-icon">>
<<if !$gymMG.locked and !$gymMG.feedbackMode>>
<<set $gymMG.locked = true>>
<<set $gymMG.picked = $gymMG.choices[0]>>
<<set $gymMG.timedOut = false>>
<<if $gymMG.picked === $gymMG.zone>>
<<set $gymMG.lastResult = "correct">>
<<set $gymMG.score += 1>>
<<else>>
<<set $gymMG.lastResult = "wrong">>
<</if>>
<<set $gymMG.phase += 1>>
<<set $gymMG.feedbackMode = true>>
<</if>>
<</btn>>
<<btn _btn1 "gymMiniGameRound" "gym-zone-btn gym-zone-btn-icon">>
<<if !$gymMG.locked and !$gymMG.feedbackMode>>
<<set $gymMG.locked = true>>
<<set $gymMG.picked = $gymMG.choices[1]>>
<<set $gymMG.timedOut = false>>
<<if $gymMG.picked === $gymMG.zone>>
<<set $gymMG.lastResult = "correct">>
<<set $gymMG.score += 1>>
<<else>>
<<set $gymMG.lastResult = "wrong">>
<</if>>
<<set $gymMG.phase += 1>>
<<set $gymMG.feedbackMode = true>>
<</if>>
<</btn>>
<<btn _btn2 "gymMiniGameRound" "gym-zone-btn gym-zone-btn-icon">>
<<if !$gymMG.locked and !$gymMG.feedbackMode>>
<<set $gymMG.locked = true>>
<<set $gymMG.picked = $gymMG.choices[2]>>
<<set $gymMG.timedOut = false>>
<<if $gymMG.picked === $gymMG.zone>>
<<set $gymMG.lastResult = "correct">>
<<set $gymMG.score += 1>>
<<else>>
<<set $gymMG.lastResult = "wrong">>
<</if>>
<<set $gymMG.phase += 1>>
<<set $gymMG.feedbackMode = true>>
<</if>>
<</btn>>
<<btn _btn3 "gymMiniGameRound" "gym-zone-btn gym-zone-btn-icon">>
<<if !$gymMG.locked and !$gymMG.feedbackMode>>
<<set $gymMG.locked = true>>
<<set $gymMG.picked = $gymMG.choices[3]>>
<<set $gymMG.timedOut = false>>
<<if $gymMG.picked === $gymMG.zone>>
<<set $gymMG.lastResult = "correct">>
<<set $gymMG.score += 1>>
<<else>>
<<set $gymMG.lastResult = "wrong">>
<</if>>
<<set $gymMG.phase += 1>>
<<set $gymMG.feedbackMode = true>>
<</if>>
<</btn>>
<<btn _btn4 "gymMiniGameRound" "gym-zone-btn gym-zone-btn-icon">>
<<if !$gymMG.locked and !$gymMG.feedbackMode>>
<<set $gymMG.locked = true>>
<<set $gymMG.picked = $gymMG.choices[4]>>
<<set $gymMG.timedOut = false>>
<<if $gymMG.picked === $gymMG.zone>>
<<set $gymMG.lastResult = "correct">>
<<set $gymMG.score += 1>>
<<else>>
<<set $gymMG.lastResult = "wrong">>
<</if>>
<<set $gymMG.phase += 1>>
<<set $gymMG.feedbackMode = true>>
<</if>>
<</btn>>
</div>
<</if>>
<!-- Phase dots -->
<div class="gym-mg-dots">
<<for _di = 1; _di <= $gymMG.totalPhases; _di++>>
<div class="gym-mg-dot <<if _di < $gymMG.phase>>gym-mg-dot-done<<elseif _di === $gymMG.phase>>gym-mg-dot-active<<else>>gym-mg-dot-pending<</if>>"></div>
<</for>>
</div>
</div>
<<script>>
(function () {
var V = State.variables;
var mg = V.gymMG;
if (!mg) { Engine.play('gym'); return; }
/* Stop any leftover timer from a previous round */
if (window.GymTimer && typeof window.GymTimer.stop === 'function') {
window.GymTimer.stop();
}
if (window._gymMGFeedbackTimeout) {
clearTimeout(window._gymMGFeedbackTimeout);
window._gymMGFeedbackTimeout = null;
}
/* Feedback shown in this same passage, then auto-advance */
if (mg.feedbackMode) {
window._gymMGFeedbackTimeout = setTimeout(function () {
mg.feedbackMode = false;
var dest = (mg.phase > mg.totalPhases) ? 'gymMiniGameResult' : 'gymMiniGameRound';
Engine.play(dest);
}, 700);
return;
}
/* Defer timer start until the passage DOM is actually mounted */
var startTimer = function () {
if (!window.GymTimer) return;
window.GymTimer.start({
duration: 5000,
barSel: '#gymMGTimerBar',
txtSel: '#gymMGTimerText',
onTimeout: function () {
var V2 = State.variables;
var mg2 = V2.gymMG;
if (!mg2 || mg2.locked) return;
mg2.locked = true;
mg2.timedOut = true;
mg2.picked = null;
mg2.lastResult = 'timeout';
mg2.phase += 1;
mg2.feedbackMode = true;
Engine.play('gymMiniGameRound');
}
});
};
/* Wait until SugarCube attaches the passage to the DOM, then start */
if (typeof jQuery !== 'undefined') {
jQuery(document).one(':passagedisplay.gymMG', function () {
setTimeout(startTimer, 0);
});
} else {
setTimeout(startTimer, 0);
}
})();
<</script>>
<</nobr>>
<<nobr>>
<<silently>>
<<set $location = "gym">>
<<if ndef $gameSettings>><<set $gameSettings = {}>><</if>>
<<set $gameSettings.gymMiniGameEnabled = false>>
<<set _zone = $gymMG ? $gymMG.zone : "upper">>
/* Flat reward when mini-game is turned off */
<<if _zone === "upper">>
<<gainStat "upperBody" 1.0>>
<<gainStat "core" 0.5>>
<<set _zoneLabel = "Upper Body">>
<<elseif _zone === "core">>
<<gainStat "core" 1.0>>
<<gainStat "lowerBody" 0.5>>
<<set _zoneLabel = "Core">>
<<elseif _zone === "lower">>
<<gainStat "lowerBody" 1.0>>
<<gainStat "cardio" 0.5>>
<<set _zoneLabel = "Lower Body">>
<<else>>
<<gainStat "cardio" 1.0>>
<<gainStat "lowerBody" 0.5>>
<<set _zoneLabel = "Cardio">>
<</if>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative "Workout Complete">>
You skip the minigame and finish a solid <<print _zoneLabel>> session. The gym mini-game is now turned off, you can re-enable it anytime from Settings.
<</narrative>>
<div class="location-actions">
<<btn "Return to Gym" "gym">><</btn>>
</div>
<</nobr>>
<<nobr>>
<<silently>>
<<set $location = "gym">>
<<set _zone = ($gymMG && $gymMG.zone) ? $gymMG.zone : "upper">>
<<set _gymVidDefs = {
upper: { folder: "upper", prefix: "upperbody", max: 13, size60: [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12] },
core: { folder: "core", prefix: "core", max: 6, size60: [1, 3] },
lower: { folder: "lower", prefix: "lowerbody", max: 13, size60: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] },
cardio: { folder: "cardio", prefix: "cardio", max: 8, size60: [1, 2, 6, 7, 8] }
}>>
<<set _gymVidDef = _gymVidDefs[_zone] || _gymVidDefs.upper>>
<<set _gymVidVariant = random(1, _gymVidDef.max)>>
<<set _gymVid = "assets/content/scenes/global/gym/" + _gymVidDef.folder + "/" + _gymVidDef.prefix + _gymVidVariant + ".mp4">>
<<set _gymVidSize = "100%">>
<<if _gymVidDef.size60.includes(_gymVidVariant)>>
<<set _gymVidSize = "60%">>
<</if>>
/* Flat reward when mini-game is turned off */
<<if _zone === "upper">>
<<gainStat "upperBody" 1.0>>
<<gainStat "core" 0.5>>
<<set _zoneLabel = "Upper Body Training">>
<<elseif _zone === "core">>
<<gainStat "core" 1.0>>
<<gainStat "lowerBody" 0.5>>
<<set _zoneLabel = "Core Training">>
<<elseif _zone === "lower">>
<<gainStat "lowerBody" 1.0>>
<<gainStat "cardio" 0.5>>
<<set _zoneLabel = "Lower Body Training">>
<<else>>
<<gainStat "cardio" 1.0>>
<<gainStat "lowerBody" 0.5>>
<<set _zoneLabel = "Cardio Session">>
<</if>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative _zoneLabel>>
<<switch _zone>>
<<case "upper">>
You run through a solid upper body session. Bench, rows, shoulder press. You hit every muscle group and finish with a good pump.
<<case "core">>
Planks, crunches, leg raises. You work through the full circuit. Your midsection burns by the end, a sign it was worth it.
<<case "lower">>
Squats, lunges, deadlifts. Your legs get a proper workout today. You push through the burn and finish strong.
<<default>>
Treadmill sprints, bike intervals, jump rope. Your heart pounds and sweat drips by the time you finish. You feel completely spent in the best way.
<</switch>>
<</narrative>>
<<vid _gymVid _gymVidSize>>
<div class="location-actions">
<<btn "Back" "gym">><</btn>>
</div>
<</nobr>>
/* ==========================================
GYM WIDGETS
Gym workout flow without transit passages
========================================== */
<<widget "gymMembershipStart">><<nobr>>
<<gymMembershipCheck>>
<<if _gymMembershipActive>><<return>><</if>>
<<set $flags = $flags || {}>>
<<set $flags.gymMembershipActive = true>>
<<set $gymMembershipDaysRemaining = 30>>
<<set $gymMembershipStartedAt = { year: $timeSys.year, month: $timeSys.month, day: $timeSys.day }>>
<</nobr>><</widget>>
<<widget "gymMembershipAdvanceDays">><<nobr>>
<<set _daysPassed = parseInt($args[0], 10) || 1>>
<<set $flags = $flags || {}>>
<<if ndef $gymMembershipDaysRemaining>><<set $gymMembershipDaysRemaining = $flags.gymMembershipActive ? 30 : 0>><</if>>
<<if $flags.gymMembershipActive>>
<<set $gymMembershipDaysRemaining = Math.max(0, ($gymMembershipDaysRemaining || 0) - _daysPassed)>>
<<if $gymMembershipDaysRemaining <= 0>>
<<set $flags.gymMembershipActive = false>>
<<set $gymMembershipDaysRemaining = 0>>
<</if>>
<</if>>
<</nobr>><</widget>>
<<widget "gymMembershipCheck">><<nobr>>
<<set $flags = $flags || {}>>
<<if ndef $gymMembershipDaysRemaining>><<set $gymMembershipDaysRemaining = $flags.gymMembershipActive ? 30 : 0>><</if>>
<<if $flags.gymMembershipActive && ($gymMembershipDaysRemaining || 0) <= 0>>
<<set $flags.gymMembershipActive = false>>
<<set $gymMembershipDaysRemaining = 0>>
<</if>>
<<set _gymMembershipActive = ($flags.gymMembershipActive && ($gymMembershipDaysRemaining || 0) > 0)>>
<</nobr>><</widget>>
/* GYM START WORKOUT - Single-step routing called from the gym button body.
Usage (inside <<btn>>): <<gymStartWorkout "upper" 15 10 "gymUpperDone">>
*/
<<widget "gymStartWorkout">><<nobr>>
<<set _zone = $args[0]>>
<<set _energyCost = parseInt($args[1])>>
<<set _stressCost = parseInt($args[2])>>
<<set _dailyKey = $args[3]>>
<<set _energyMsg = ($args[4] && $args[4] !== "") ? $args[4] : "Not enough energy to work out.">>
<<gymMembershipCheck>>
<<if !_gymMembershipActive>><<goto "gym">><</if>>
<<requireMinEnergy _energyCost "gym" _energyMsg>>
<<if $daily[_dailyKey]>><<goto "gym">><</if>>
<<set $daily[_dailyKey] = true>>
<<advanceTime 45 "relax">>
<<loseStat "energy" _energyCost>>
<<loseStat "stress" _stressCost>>
<<set $gymMG = { zone: _zone, phase: 1, score: 0, totalPhases: 4, lastResult: "", timedOut: false, picked: null }>>
<<if $gameSettings.gymMiniGameEnabled isnot false>>
<<if !$flags.gymMiniGameTutorialSeen>>
<<goto "gymWorkoutTutorial">>
<<else>>
<<goto "gymMiniGameRound">>
<</if>>
<<else>>
<<goto "gymWorkoutOff">>
<</if>>
<</nobr>><</widget>>
/* GYM WORKOUT BUTTON - Daily lock + sporty outfit check + minimum energy check.
Usage: <<gymWorkoutBtn "upper">>
Zones: "upper" | "core" | "lower" | "cardio"
*/
<<widget "gymWorkoutBtn">><<nobr>>
<<silently>>
<<set _zone = $args[0]>>
/* Zone config */
<<switch _zone>>
<<case "upper">>
<<set _btnText = "Upper Body">>
<<set _dailyDone = $daily.gymUpperDone>>
<<set _minEnergy = 15>>
<<case "core">>
<<set _btnText = "Core">>
<<set _dailyDone = $daily.gymCoreDone>>
<<set _minEnergy = 15>>
<<case "lower">>
<<set _btnText = "Lower Body">>
<<set _dailyDone = $daily.gymLowerDone>>
<<set _minEnergy = 15>>
<<case "cardio">>
<<set _btnText = "Cardio">>
<<set _dailyDone = $daily.gymCardioDone>>
<<set _minEnergy = 18>>
<<default>>
<<set _btnText = "Workout">>
<<set _dailyDone = false>>
<<set _minEnergy = 15>>
<</switch>>
<<set _canDo = true>>
<<set _tooltipText = "">>
/* Membership check */
<<gymMembershipCheck>>
<<if !_gymMembershipActive>>
<<set _canDo = false>>
<<set _tooltipText = "Requires an active gym membership">>
<</if>>
/* Daily check */
<<if _canDo && _dailyDone>>
<<set _canDo = false>>
<<set _tooltipText = "Already done today">>
<</if>>
/* Outfit check (sporty, 3 slots) */
<<if _canDo>>
<<checkOutfitStyle "sporty" 3 0>>
<<if !State.temporary.outfitCheckResult.allowed>>
<<set _canDo = false>>
<<set _tooltipText = State.temporary.outfitCheckResult.reason>>
<</if>>
<</if>>
/* Energy check */
<<if _canDo && (parseInt($energy || 0) < _minEnergy)>>
<<set _canDo = false>>
<<set _tooltipText = "Need " + _minEnergy + " energy">>
<</if>>
<</silently>>
<<if _canDo>>
<<switch _zone>>
<<case "upper">>
<<btn "Upper Body">><<gymStartWorkout "upper" 15 10 "gymUpperDone">><</btn>>
<<case "core">>
<<btn "Core">><<gymStartWorkout "core" 15 10 "gymCoreDone">><</btn>>
<<case "lower">>
<<btn "Lower Body">><<gymStartWorkout "lower" 15 10 "gymLowerDone">><</btn>>
<<case "cardio">>
<<btn "Cardio">><<gymStartWorkout "cardio" 18 15 "gymCardioDone" "Not enough energy for a cardio session.">><</btn>>
<</switch>>
<<else>>
<<set _lockedSpan = '<span class="link-internal btn-style btn-default locked" data-tooltip="' + _tooltipText + '"><i class="icon icon-lock icon-12"></i> ' + _btnText + '</span>'>>
<<print _lockedSpan>>
<</if>>
<</nobr>><</widget>>
<div class="divnobr">
<<nobr>>
<<silently>>
<<set $location = "gym">>
<<set $flags = $flags || {}>>
<<if !$characters.gymTrainerNick.known>>
<<set $characters.gymTrainerNick.known = true>>
<</if>>
<</silently>>
<<narrative "Iron Works Gym">>
Nora stepped out from behind the desk and led you inside. Past the turnstile, the main floor opened up in front of you. Free weights, cable stations, and squat racks. Someone in the back corner finished a set, and the bar hit the floor with a sound that carried across the room.
<</narrative>>
<<dialog "gymReceptionNora">>We're open from eight to ten, every day.<</dialog>>
<<dialog "player">>Does it usually get crowded?<</dialog>>
<<dialog "gymReceptionNora">>Mornings get busier, afternoons are calmer. Depends on your routine.<</dialog>>
<<narrative>>
Nora called out toward the gym floor.
<</narrative>>
<<dialog "gymReceptionNora">>Nick!<</dialog>>
<<narrative>>
The guy by the squat rack looked up and walked over to you at a quick pace.
<</narrative>>
<<dialog "gymReceptionNora">>Nick, this is $playerName. I'm showing her around and wanted you two to meet.<</dialog>>
<<dialog "gymTrainerNick">>Nice to meet you, $playerName. Have you trained at a gym before?<</dialog>>
<<dialog "player">>Not really. I've mostly been running outside.<</dialog>>
<<dialog "gymTrainerNick">>That's still a start. But lifting here will make you feel stronger, trust me.<</dialog>>
<<narrative>>
He was about to continue when his eyes caught something across the floor.
<</narrative>>
<<dialog "gymTrainerNick">>You're doing that movement wrong!<</dialog>>
<<narrative>>
The guy in the corner paused. Nick turned back to you.
<</narrative>>
<<dialog "gymTrainerNick">>Anyway, I gotta go. If you want a workout plan, come find Nora or me.<</dialog>>
<<narrative>>
He turned and left. Nora gave a small laugh.
<</narrative>>
<<dialog "gymReceptionNora">>Nick gets intense. He's always like this. Want to keep going?<</dialog>>
<div class="location-actions">
<<btn "Continue" "quest_lily_gym_frontdesk_intro_part3">><</btn>>
</div>
<</nobr>>
</div>
<div class="divnobr">
<<nobr>>
<<silently>>
<<discover "GymFemaleLocker" "Women's Locker Room">>
<<discover "GymFemaleLockerWardrobe" "Locker">>
<<discover "GymMaleLocker" "Men's Locker Room">>
<<discover "GymMaleLockerShower" "Men's Shower">>
<<set $location = "gymFemaleLocker">>
<</silently>>
<<narrative "Women's Locker Room">>
Nora led you down the hallway and nudged a door open with her shoulder.
<</narrative>>
<<dialog "gymReceptionNora">>This is the locker room. You can stash your clothes and personal stuff here.<</dialog>>
<<narrative>>
She opened one of the nearby lockers and showed you inside. Clean, simple, enough space.
<</narrative>>
<<dialog "gymReceptionNora">>Lockers run on a card system. If you sign up, we give you your own access card.<</dialog>>
<<dialog "player">>Got it.<</dialog>>
<<dialog "gymReceptionNora">>Come on, let's keep going.<</dialog>>
<<narrative>>
You moved toward the shower and restroom area. A thin ribbon of steam curled out from under one of the doors. Someone was inside.
<</narrative>>
<<narrative>>
On the counter by the sink, a hair dryer, a perfume bottle, and a half-used moisturizer were left out.
<</narrative>>
<<dialog "gymReceptionNora">>Mirror station is here too, if you want to freshen up after training.<</dialog>>
<<narrative>>
Nora turned toward the door and added over her shoulder as she stepped out.
<</narrative>>
<<dialog "gymReceptionNora">>Men's side is right next door. Same layout - less perfume, more ego. (laughing)<</dialog>>
<div class="location-actions">
<<btn "Continue" "quest_lily_gym_frontdesk_intro_part4">><</btn>>
</div>
<</nobr>>
</div>
<div class="divnobr">
<<nobr>>
<<silently>>
<<set $location = "gym">>
<<set $flags = $flags || {}>>
<<set _resolveQuest = ($questState && $questState.active && $questState.active.lily_gym_intro && $questState.active.lily_gym_intro.stage === 1)>>
<<if _resolveQuest>>
<<set $flags.lilyGymReceptionTalked = true>>
<<gainCharacterStat "gymReceptionNora" "friendship" 1>>
<<completeQuest "lily_gym_intro">>
<<advanceTime 20>>
<<loseStat "energy" 2>>
<<gainStat "mood" 1>>
<<recalculateStats>>
<<updateCharacterLocations>>
<</if>>
<</silently>>
<<narrative "Iron Works Gym">>
Nora guided you back to the front desk and opened a brochure, turning the pricing row toward you.
<</narrative>>
<<gymMembershipCheck>>
<<if _gymMembershipActive>>
<<dialog "gymReceptionNora">>You're already set up. Your membership is active.<</dialog>>
<div class="location-actions">
<<btn "Back" "gym">><</btn>>
</div>
<<else>>
<<dialog "gymReceptionNora">>Standard membership is 229 dollars per month.<</dialog>>
<<narrative>>
You paused for a second when you saw the number.
<</narrative>>
<<dialog "player">>229 a month...<</dialog>>
<<dialog "gymReceptionNora">>Yeah, if you want, we can start it now. If not, you can always come back later.<</dialog>>
<<narrative>>
Nora waited without rushing you.
<</narrative>>
<div class="location-actions">
<<if $cashBalance gte 229>>
<<btn "Join (229$)" "gym">>
<<spendCash 229>>
<<if _transactionSuccess>><<gymMembershipStart>><</if>>
<</btn>>
<<else>>
<span class="link-internal btn-style btn-default locked mirror-action-muted" data-tooltip="Not enough money (need 229$)"><i class="icon icon-lock icon-12"></i> Join (229$)</span>
<</if>>
<<btn "Back" "gym">><</btn>>
</div>
<</if>>
<</nobr>>
</div>
<<nobr>>
<<silently>>
<<set $location = "hospital">>
<<if ndef $characters.doctorAdrianCole>><<domInclude "charDoctorAdrianColeHospital">><</if>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set _doctorSeenToday = ($hospitalLastDoctorDayKey || 0) === _dateKey>>
<<set _canTreat = (($health || 0) < 100) && (($cashBalance || 0) >= 15) && !_doctorSeenToday>>
<<if _canTreat>>
<<spendCash 15>>
<<if _transactionSuccess>>
<<set $hospitalLastDoctorDayKey = _dateKey>>
<<set $health = 100>>
<<loseStat "stress" 8>>
<<gainStat "mood" 6>>
<<recalculateStats>>
<</if>>
<</if>>
<</silently>>
<<narrative "St. Luke's Hospital">>
<<if _canTreat && _transactionSuccess>>
Doctor Adrian Cole reviews your chart, checks your vitals, and runs a quick examination. After a short treatment and observation, your body feels stable again.
<</narrative>>
<<dialog "doctorAdrianCole">>You're good to go now. Take it easy and don't push yourself too hard today.<</dialog>>
<<else>>
You check in at the desk, but they can't proceed right now.
<</narrative>>
<<if ($health || 0) >= 100>>
<<dialog "doctorAdrianCole">>Your health is already fine. You don't need treatment at the moment.<</dialog>>
<<elseif _doctorSeenToday>>
<<dialog "doctorAdrianCole">>I already treated you today. Go home, rest, and come back tomorrow if you still feel unwell.<</dialog>>
<<else>>
<<dialog "doctorAdrianCole">>Treatment fee is 15 dollars. Come back when you have enough cash.<</dialog>>
<</if>>
<</if>>
<div class="location-actions">
<<btn "Back to Hospital" "hospital">><</btn>>
</div>
<</nobr>>
/* ==========================================
ST. LUKE'S HOSPITAL Doctor
Dialog id: doctorAdrianCole
========================================== */
<<set setup.characterDefs.doctorAdrianCole = {
firstName: "Adrian",
lastName: "Cole",
birthYear: 1988,
gender: "male",
occupation: "Doctor",
location: "St. Luke's Hospital",
avatar: "assets/content/people/oldtown/hospital/doctorAdrianCole.webp",
type: "npc",
color: "#2563eb",
info: "<p>A composed physician at St. Luke's Hospital. He handles routine examinations and quick follow-up treatments.</p>",
status: "Attending Physician",
relationship: { status: "single" }
}>>
<<set $characters.doctorAdrianCole = {
stats: { love: 0, loveLevel: 1, friendship: 0, friendshipLevel: 1, lust: 0, lustLevel: 1, trust: 0, trustLevel: 1 },
firstMet: null,
known: false,
currentLocation: null,
currentStatus: null
}>>
<<set setup.schedules.doctorAdrianCole = {
weekday: [
{ hour: 0, location: "hospital", status: "working" }
],
weekend: [
{ hour: 0, location: "hospital", status: "working" }
]
}>>
<<nobr>>
<<silently>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set _tier = 0>>
<<if $flags.bodyAwarenessTier3>>
<<set _tier = 3>>
<<elseif $flags.bodyAwarenessTier2>>
<<set _tier = 2>>
<<elseif $flags.bodyAwarenessTier1>>
<<set _tier = 1>>
<</if>>
<<set _roll = random(1, 4)>>
<<set _gain = 1>>
<<set _text = "You give yourself a quick look in the mirror, adjust your towel, and move on.">>
<<if _tier === 3>>
<<set _gain = random(3, 4)>>
<<if _roll === 1>>
<<set _text = "Your gaze stays on the mirror longer than before. You are not checking anymore. You are watching: wet hair, low towel, the curve of your waist. You like it.">>
<<elseif _roll === 2>>
<<set _text = "Your hand moves to fix the towel, then stops at your waist. You look too good in the mirror to rush. The thought does not embarrass you as much as it used to.">>
<<elseif _roll === 3>>
<<set _text = "You catch your own eyes in the mirror and do not look away. Wet, a little messy, undeniably attractive. Thinking that feels easier now.">>
<<else>>
<<set _text = "You pull the towel tighter against your chest, but your eyes stay on your reflection. The wet shine of your skin and the shape of your hips hold you there a second too long.">>
<</if>>
<<elseif _tier === 2>>
<<set _gain = random(2, 3)>>
<<if _roll === 1>>
<<set _text = "You turn sideways and look at the line of your hips in the mirror. Normally you would turn away. This time, you stay for two more seconds.">>
<<elseif _roll === 2>>
<<set _text = "The towel loosens at your waist. Before fixing it, you look at the shape in the mirror. Your hips, waist, and legs seem to fit together better now. Your face warms.">>
<<elseif _roll === 3>>
<<set _text = "You lean against the sink and look yourself over. The change is not just numbers anymore. Even the way you stand in the mirror feels different.">>
<<else>>
<<set _text = "You toss your wet hair back and your chest shifts under the towel. Your eyes drop there for a second, then you catch yourself with a small smile.">>
<</if>>
<<elseif _tier === 1>>
<<set _gain = random(1, 2)>>
<<if _roll === 1>>
<<set _text = "Before the steam covers the mirror again, you catch a quick look at yourself. The small change at your waist is still there. Knowing it leaves a strange warmth behind.">>
<<elseif _roll === 2>>
<<set _text = "While adjusting the towel, your reflection catches your eye. You pause on a detail you would have ignored before. Maybe stopping is part of the change too.">>
<<elseif _roll === 3>>
<<set _text = "You sweep your wet hair back. In the mirror, your shoulders and waist look a little clearer. It is not much, but you like it anyway.">>
<<else>>
<<set _text = "You hold the towel against your chest and look at the wet shine of your skin. Your gaze lingers for one second too long before you move on.">>
<</if>>
<</if>>
<<set $afterShowerMirrorLastDayKey = _dateKey>>
<<gainStat "arousal" _gain>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative "Bathroom Mirror">>
<<print _text>>
<</narrative>>
<</nobr>>
<<nobr>>
<<silently>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set $flags.bodyAwarenessTier1 = true>>
<<set $afterShowerMirrorLastDayKey = _dateKey>>
<<gainStat "arousal" 3>>
<<gainStat "mood" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative "Bathroom Mirror">>
Steam covers the mirror completely. You wipe a small clear patch with your hand.
Your face appears first. Wet hair sticks to your neck, and your lips look fuller and pinker from the heat of the shower.
Your eyes drift down.
The towel sits above your chest, but water still trails down your skin. The curve of your waist looks clearer than before, and your hips feel heavier under the towel.
For a few seconds, you only look.
Seeing yourself like this feels strange. A little embarrassing.
But also strangely nice.
Your hand moves to your chest to adjust the towel. Your fingers linger on the fabric for longer than they need to. Your skin is still warm.
Then you look away.
<</narrative>>
<</nobr>>
<<nobr>>
<<silently>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set $flags.bodyAwarenessTier2 = true>>
<<set $afterShowerMirrorLastDayKey = _dateKey>>
<<gainStat "arousal" 5>>
<<gainStat "mood" 1>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative "Bathroom Mirror">>
You turn sideways in the mirror. You realize you are doing it on purpose, but you do not stop.
The towel sits loose around your waist. The roundness of your hips is clearer now, and your legs look longer with your skin still wet. Drops of water slide down the dip of your back.
Slowly, you push your hips back a little.
The reflection surprises you.
You have not just changed. You look sexier.
The thought sends a small, warm wave through your lower stomach.
Your hand moves to the edge of the towel. You should fix it, but your fingers stay on the fabric for a while. You keep watching yourself from this angle.
Then you take a deep breath and break your gaze.
<</narrative>>
<</nobr>>
<<nobr>>
<<silently>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set $flags.bodyAwarenessTier3 = true>>
<<set $afterShowerMirrorLastDayKey = _dateKey>>
<<gainStat "arousal" 7>>
<<gainStat "mood" 2>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative "Bathroom Mirror">>
You do not leave the mirror right away.
Water drips from your hair, down your shoulder, toward your chest. Your eyes follow the drop, then stay on the curve of your breasts, your waist, your hips.
The towel has slipped a little lower. You should fix it.
But first, you look.
Your waist is more defined, your hips fuller, your legs longer in the mirror. Your wet skin glows softly in the steam.
Your hand moves to the edge of the towel. You hold it, but you do not pull right away.
For a moment, you see yourself through someone else's eyes.
You can almost guess where they would look.
Your face heats.
But you do not look away from the mirror.
Slowly, you fix the towel.
<</narrative>>
<</nobr>>
<<nobr>>
<<set _returnPassage = $porn.returnPassage || "brotherComputerMenu">>
<<requireMinEnergy 15 _returnPassage>>
<<silently>>
<<if $porn.source === "brotherRoom">>
<<set $location = "fhBrotherRoom">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<set $flags.brotherPornFirstSeen = true>>
<<startQuest "brother_browser_rabbit_hole">>
<<gainStat "arousal" 4>>
<<gainStat "stress" 2>>
<<loseStat "energy" 5>>
<<advanceTime 30>>
<<flushNotifications>>
<</silently>>
<<narrative "Browsing">>
You open the browser for something ordinary. Homework, a quick search, anything normal enough to excuse sitting at his desk.
<</narrative>>
<<narrative>>
The page restores faster than you expect. For a second it is just a blank tab, then the address bar drops down with old suggestions. A few of them are normal. Games, videos, random searches.
<</narrative>>
<<narrative>>
Then you see one that is not.
<</narrative>>
<<narrative>>
A porn title sits in the list, half cut off but still clear enough. The little site icon beside it makes your stomach tighten. You do not click it. You only stare for a second too long.
<</narrative>>
<<dialog "player">>
(Inner voice)
Wait. Is he watching this?
<</dialog>>
<<narrative>>
Your hand moves before you finish the thought. You close the suggestions, then the browser itself, like shutting the screen fast enough might erase what you saw.
<</narrative>>
<<narrative>>
The room is quiet. The chair feels warmer under you than it should. Your heart keeps beating too hard.
<</narrative>>
<<narrative>>
You tell yourself it is normal. Guys watch porn. Brothers probably watch porn. That does not mean anything.
<</narrative>>
<<narrative>>
But the list is still in your head.
<</narrative>>
<div class="location-actions">
<<btn "Go Back" _returnPassage>><</btn>>
</div>
<</nobr>>
<<nobr>>
<<set _returnPassage = $porn.returnPassage || "brotherComputerMenu">>
<<requireMinEnergy 15 _returnPassage>>
<<silently>>
<<if $porn.source === "brotherRoom">>
<<set $location = "fhBrotherRoom">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set $brotherPornSecondLastSeenDayKey = _dateKey>>
<<set _step = ($brotherPornPhase2Step || 0) + 1>>
<<set _allowIncest = ($contentPreferences && $contentPreferences.incest !== false)>>
<<if _step === 1>>
<<set _deepArousal = 5>>
<<elseif _step === 2>>
<<set _deepArousal = 6>>
<<elseif _step === 3>>
<<set _deepArousal = 12>>
<<else>>
<<set _deepArousal = 16>>
<</if>>
<</silently>>
<<if _step === 1>>
<<narrative "Browsing">>
You sit at your brother's computer.
<</narrative>>
<<narrative>>
The browser opens. You were going to search for something. At least, that is what you tell yourself.
<</narrative>>
<<narrative>>
For a few seconds you just stare at the blank page. Then your hand moves to the history.
<</narrative>>
<<dialog "player">>
(Inner voice)
I'm just going to see what he watched.
<</dialog>>
<<narrative>>
The history opens. At the top there are games, videos, normal searches. A little lower, the titles start to change. Small site icons, half-cut sentences, words that are too clear.
<</narrative>>
<<narrative>>
A line says "teen." The rest of the title is cut off. The little thumbnail beside it shows a bare shoulder, messy hair, half a face.
<</narrative>>
<<narrative>>
You pause for a moment. Then you click.
<</narrative>>
<<narrative>>
The video opens and a short moan comes through the speakers. You lower the volume right away. Your heart jumps like you were too late.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcteen.mp4" "100%">>
<<narrative>>
On the screen, a young adult woman is lying on her back. Her tits shake with every movement. Her legs are open. The man over her has her pressed into the bed.
<</narrative>>
<<narrative>>
First you look at her. Her face is flushed, her breathing broken. One hand is gripping the sheet.
<</narrative>>
<<narrative>>
Then you look at him. He looks bigger above her. His arms are planted on either side of her. His hips move in a steady rhythm.
<</narrative>>
<<narrative>>
He drives his cock hard into her pussy.
<</narrative>>
<<dialog "player">>
(Inner voice)
I'm really watching this.
<</dialog>>
<<narrative>>
You should close it. You don't.
<</narrative>>
<<narrative>>
Her voice is still audible even with the volume low. The man's rhythm gets a little faster.
<</narrative>>
<<narrative>>
Your eyes catch on his cock. As it slides in and out, her pussy tightens around it and the wetness catches the light. Her legs open a little wider. Her ass shifts against the bed.
<</narrative>>
<<narrative>>
A warm feeling wakes low in your stomach.
<</narrative>>
<<narrative>>
Your face burns when you notice it.
<</narrative>>
<<narrative>>
For a second, you think about your own body. You realize you have no idea what something like that would feel like inside you.
<</narrative>>
<<dialog "player">>
(Inner voice)
How would something like that really feel?
<</dialog>>
<<narrative>>
You watch for one more second. Then you close the tab.
<</narrative>>
<<narrative>>
You stare at the blank screen. Your heart is still fast. There is a faint, uncomfortable heat between your legs.
<</narrative>>
<<narrative>>
It does not feel like the real problem is that he watched it.
<</narrative>>
<<narrative>>
The real problem is that you felt something while watching it too.
<</narrative>>
<<elseif _step === 2>>
<<if _allowIncest>>
<<narrative "Browsing">>
You sit at your brother's computer again.
<</narrative>>
<<narrative>>
This time your hand moves faster once the browser opens. You don't linger on the blank page. You open the history.
<</narrative>>
<<dialog "player">>
(Inner voice)
What else does he watch?
<</dialog>>
<<narrative>>
The list comes up. There are normal things near the top, but you barely read them now. Your eyes move straight down.
<</narrative>>
<<narrative>>
The porn titles are still there.
<</narrative>>
<<narrative>>
A little lower, you see a line that starts with a "mom" tag. Your finger stops.
<</narrative>>
<<dialog "player">>
(Inner voice)
What kind of category is that?
<</dialog>>
<<narrative>>
You should not click it.
<</narrative>>
<<narrative>>
You click it.
<</narrative>>
<<narrative>>
The video opens and you lower the volume right away. You are a little more prepared this time, but your heart still speeds up.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcmom.mp4" "100%">>
<<narrative>>
On the screen, there is a middle-aged woman. Her full tits shake as she stays on her hands and knees. The young man behind her has both hands gripping her hips.
<</narrative>>
<<narrative>>
She turns her head to the side. Her face is flushed, her mouth open. With every thrust, her voice comes out low and rough.
<</narrative>>
<<narrative>>
He drives his cock hard into her pussy. Her ass jerks back with every push, like pain and pleasure are mixing together.
<</narrative>>
<<narrative>>
Then she says something under her breath.
<</narrative>>
<<narrative>>
"Son..."
<</narrative>>
<<narrative>>
Your stomach tightens.
<</narrative>>
<<dialog "player">>
(Inner voice)
What did she just say?
<</dialog>>
<<narrative>>
You should close it. But you don't want to.
<</narrative>>
<<narrative>>
The man's hands grip her hips tighter. The woman moans louder, her tits moving with every thrust.
<</narrative>>
<<narrative>>
You think about why your brother opened this.
<</narrative>>
<<narrative>>
Then the thought slips somewhere else.
<</narrative>>
<<dialog "player">>
(Inner voice)
Could he think things like that about Mom too?
<</dialog>>
<<narrative>>
Your face heats up as soon as the question appears. You don't want an answer, but the question doesn't leave.
<</narrative>>
<<narrative>>
The woman moans again. The word in the video does not stay inside the video anymore. For a moment, the rooms in the house, the dinner table, and the sounds through half-open doors all mix in your head.
<</narrative>>
<<dialog "player">>
(Inner voice)
Could I look at someone in the family like that too?
<</dialog>>
<<narrative>>
The thought settles low in your stomach.
<</narrative>>
<<narrative>>
You close the tab.
<</narrative>>
<<narrative>>
For a while you only listen to the quiet. Every sound in the house feels too close all at once.
<</narrative>>
<<narrative>>
You don't look at the screen.
<</narrative>>
<<narrative>>
Because if you do, you will remember what you just thought.
<</narrative>>
<<else>>
<<narrative "Browsing">>
You are looking through your brother's history again.
<</narrative>>
<<narrative>>
This time you know where to look a little better. You skip past the normal searches quickly.
<</narrative>>
<<dialog "player">>
(Inner voice)
What else does he watch?
<</dialog>>
<<narrative>>
Among the porn titles, a line marked "MILF" catches your eye.
<</narrative>>
<<narrative>>
You pause for a moment.
<</narrative>>
<<narrative>>
You know the word. At least enough to understand what it is pointing at. Older women. Mature women. More confident, more experienced.
<</narrative>>
<<narrative>>
You click.
<</narrative>>
<<narrative>>
The video opens. A mature woman is on the bed, under a man younger than she is. Her tits move with every thrust, her hips following his rhythm. He fucks her hard, but she does not look like she is trying to get away.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcmilf.mp4" "100%">>
<<narrative>>
If anything, she looks like she knows exactly what she wants.
<</narrative>>
<<narrative>>
For a moment, you look more at her than at him.
<</narrative>>
<<narrative>>
She looks comfortable. She does not hide her voice. She does not act ashamed of her body.
<</narrative>>
<<dialog "player">>
(Inner voice)
So he likes women like this.
<</dialog>>
<<narrative>>
The man grips her hips and drives his cock into her pussy. She moans and throws her head back.
<</narrative>>
<<narrative>>
You watch for a few more seconds.
<</narrative>>
<<narrative>>
This does not hit you the same way. It feels easier to understand. Still, the fact that your brother chose this stays in your head.
<</narrative>>
<<narrative>>
You close the tab.
<</narrative>>
<<narrative>>
What stays with you is not only the video. It is the way the woman carried herself.
<</narrative>>
<<narrative>>
Open. Comfortable. Like she knew what she wanted.
<</narrative>>
<</if>>
<<elseif _step === 3>>
<<narrative "Browsing">>
The "BBC" lines catch your eye again in the history.
<</narrative>>
<<narrative>>
The same word sits in a few different titles. Your brother did not search this just once.
<</narrative>>
<<dialog "player">>
(Inner voice)
What does BBC even mean?
<</dialog>>
<<narrative>>
You click. The video opens.
<</narrative>>
<<narrative>>
On the screen, there is a young adult woman close to your age. She is sitting on a bed. Beside her stands a large Black man.
<</narrative>>
<<narrative>>
She looks small next to him. His hands are big, his shoulders wide. He takes up too much of the screen.
<</narrative>>
<<narrative>>
The woman reaches for his pants. The zipper opens. When his cock comes out, you only stare for a few seconds.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcbbc1.mp4" "100%">>
<<dialog "player">>
(Inner voice)
Can it really be that big?
<</dialog>>
<<narrative>>
The woman's smile falters. He brings his cock closer to her face, brushing it over her cheek, her lips, her chin.
<</narrative>>
<<narrative>>
She opens her mouth, tries, then pulls back.
<</narrative>>
<<narrative>>
You realize you are holding your breath too.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcbbc2.mp4" "100%">>
<<dialog "player">>
(Inner voice)
How is that supposed to fit?
<</dialog>>
<<narrative>>
You skip forward a little.
<</narrative>>
<<narrative>>
The woman is on the bed. He is over her. Your eyes move between her face, his body, and the movement between them.
<</narrative>>
<<narrative>>
She makes a sound. Short, low. You cannot tell if it is pain or pleasure.
<</narrative>>
<<narrative>>
Her face tightens, then softens. She looks like she might pull away, then lets herself go.
<</narrative>>
<<dialog "player">>
(Inner voice)
Does she like it?
<</dialog>>
<<narrative>>
You skip forward again.
<</narrative>>
<<narrative>>
This time she looks different. She is still struggling, but she is not trying to escape. His cock moves in and out of her pussy more easily now. Her voice has changed too, more open, more out of control.
<</narrative>>
<<narrative>>
You imagine yourself in her place.
<</narrative>>
<<narrative>>
Only briefly.
<</narrative>>
<<narrative>>
But it is enough.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcbbc3.mp4" "100%">>
<<dialog "player">>
(Inner voice)
What would I do?
<</dialog>>
<<narrative>>
You skip closer to the end.
<</narrative>>
<<narrative>>
The woman is on her knees. He is standing in front of her. Her mouth is open, her eyes on his cock.
<</narrative>>
<<narrative>>
He cums.
<</narrative>>
<<narrative>>
Sperm lands on her lips, her chin, and a little inside her mouth. She does not move.
<</narrative>>
<<narrative>>
She takes a breath. Then she licks the corner of her mouth.
<</narrative>>
<<narrative>>
A small smile comes to her face.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcbbc4.mp4" "100%">>
<<narrative>>
The smile does not disappear right away.
<</narrative>>
<<narrative>>
The first thought you have is not what you expected.
<</narrative>>
<<dialog "player">>
(Inner voice)
What would it taste like?
<</dialog>>
<<narrative>>
Your throat goes dry.
<</narrative>>
<<narrative>>
The video ends. You close the tab. Not quickly.
<</narrative>>
<<narrative>>
Like it is finished.
<</narrative>>
<<narrative>>
You look at the blank page. There are no answers.
<</narrative>>
<<narrative>>
Only the things left in your head.
<</narrative>>
<<dialog "player">>
(Inner voice)
How would it fit?
<</dialog>>
<<dialog "player">>
(Inner voice)
Would it hurt?
<</dialog>>
<<dialog "player">>
(Inner voice)
Would it feel good after?
<</dialog>>
<<dialog "player">>
(Inner voice)
Would the taste be bad?
<</dialog>>
<<narrative>>
For a while, you do not click anything.
<</narrative>>
<<else>>
<<if _allowIncest>>
<<narrative "Browsing">>
You sit at your brother's computer.
<</narrative>>
<<narrative>>
No excuse this time. No lie you tell yourself.
<</narrative>>
<<narrative>>
You open the browser, mute the sound immediately, and glance at the door. Then you go straight to the history.
<</narrative>>
<<narrative>>
You already know what you're looking for.
<</narrative>>
<<dialog "player">>
(Inner voice)
What does he actually like?
<</dialog>>
<<narrative>>
You scroll down. Familiar titles pass by. Some don't even make you pause anymore.
<</narrative>>
<<narrative>>
Then one word seems to catch between the lines.
<</narrative>>
<<narrative>>
...sister...
<</narrative>>
<<narrative>>
It appears once. Then again. Then again.
<</narrative>>
<<narrative>>
You click the search box. Your fingers hesitate.
<</narrative>>
<<narrative>>
sister
<</narrative>>
<<narrative>>
You hit Enter.
<</narrative>>
<<narrative>>
The list explodes. The more you scroll, the more the word multiplies. One title stands out.
<</narrative>>
<<narrative>>
"Caught by Dad While Fucking My Older Sister."
You read it twice.
<</narrative>>
<<dialog "player">>
(Inner voice)
Older sister...? Is he really searching this?
<</dialog>>
<<narrative>>
The video opens.
<</narrative>>
<<narrative>>
A messy bedroom. Half-open door. A young woman on the edge of the bed, shirt pushed up, tits bare.
<</narrative>>
<<narrative>>
The man approaches. She laughs at first, then weakly tries to push him away. When his hand grabs her tits, her breathing quickens. She doesn't pull back.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcstep1.mp4" "100%">>
<<dialog "player">>
(Inner voice)
Has he ever seen me like this?
<</dialog>>
<<narrative>>
He pushes her onto the bed, spreads her legs, and rubs his cock against her pussy. She looks down, then closes her eyes.
<</narrative>>
<<narrative>>
He enters her slowly. Her voice catches, then turns into a low, broken moan. Soon her hips lift to meet him.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcstep2.mp4" "100%">>
<<narrative>>
Your hand slides onto your thigh without thinking. Your fingers move higher. Heat builds between your legs.
<</narrative>>
<<narrative>>
You freeze. For several long seconds, your hand stays there.
<</narrative>>
<<narrative>>
Noticing it isn't the worst part.
<</narrative>>
<<narrative>>
The worst part is that you didn't pull it away immediately.
<</narrative>>
<<narrative>>
Your heart hammers. A door sound comes from the video. The woman flinches.
<</narrative>>
<<narrative>>
So do you.
<</narrative>>
<<narrative>>
You close the tab. The screen goes blank.
<</narrative>>
<<narrative>>
But the word doesn't leave.
<</narrative>>
<<narrative>>
...sister...
<</narrative>>
<<narrative>>
Why is he searching this? Does he imagine me like that?
<</narrative>>
<<narrative>>
Do men look at me that way? Without me ever noticing?
<</narrative>>
<<narrative>>
Your hand stays on the mouse.
You don't click anything.
<</narrative>>
<<else>>
<<narrative "Browsing">>
You sit at your brother's computer without looking for an excuse this time.
<</narrative>>
<<narrative>>
You open the browser, mute the sound from the start, and glance at the door. Then you go straight to the history.
<</narrative>>
<<narrative>>
You already know what you're looking for.
<</narrative>>
<<dialog "player">>
(Inner voice)
What does he actually like?
<</dialog>>
<<narrative>>
You scroll down. Familiar titles pass by. Some don't even make you pause anymore.
Then one word seems to catch between the lines.
<</narrative>>
<<narrative>>
...roommate...
<</narrative>>
<<narrative>>
It appears once. Then again.
<</narrative>>
<<narrative>>
You click the search box. Your fingers hesitate for a second.
<</narrative>>
<<narrative>>
roommate
<</narrative>>
<<narrative>>
You hit Enter.
<</narrative>>
<<narrative>>
The list fills. The more you scroll, the more the word multiplies. One title stands out from the rest.
<</narrative>>
<<narrative>>
"Caught Watching My Roommate Change." You read it twice.
<</narrative>>
<<dialog "player">>
(Inner voice)
Roommate...? He's really searching this?
<</dialog>>
<<narrative>>
The video opens.
A small room. Half-open door. A young woman changing inside the room. She is not looking at the camera.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcroom1.mp4" "100%">>
<<narrative>>
At first, it almost looks innocent. That makes it worse.
<</narrative>>
<<narrative>>
The woman pulls her shirt off. Her tits are bare. She gathers her hair while looking in the mirror, still not noticing.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcroom2.mp4" "100%">>
<<dialog "player">>
(Inner voice)
Is this how he watches her?
<</dialog>>
<<narrative>>
The man steps quietly through the door. The woman startles at first, then laughs. She tries to cover her chest, but not quite.
He moves closer. She doesn't pull away.
<</narrative>>
<<narrative>>
He pushes her onto the bed and spreads her legs. He rubs his cock against her pussy. She looks down, then closes her eyes.
<</narrative>>
<<narrative>>
He enters her slowly. Her voice catches, then turns into a low moan. Her hips begin to lift toward him.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/brotherRoom/computer/porn/brotherpcroom3.mp4" "100%">>
<<narrative>>
Your hand slides onto your thigh without thinking. Your fingers move higher. Heat builds between your legs.
<</narrative>>
<<narrative>>
You freeze. For a few seconds, your hand stays there.
<</narrative>>
<<narrative>>
The fact that you don't pull it away right away is worse.
<</narrative>>
<<narrative>>
Your heart pounds.
In the video, the woman moans louder. The man tells her to be quiet. She covers her mouth with her hand, but her smile doesn't disappear.
<</narrative>>
<<narrative>>
You look at the door. The hallway is quiet.
<</narrative>>
<<narrative>>
You close the tab. The screen goes blank. But the word doesn't leave.
<</narrative>>
<<narrative>>
...roommate...
<</narrative>>
<<narrative>>
Why is your brother searching this?
<</narrative>>
<<narrative>>
Does he look at someone just because she lives close to him?
<</narrative>>
<<narrative>>
Do men look at women in the same house like that?
<</narrative>>
<<narrative>>
Am I being watched like that too, without noticing?
<</narrative>>
<<narrative>>
Your hand stays on the mouse. For a long while, you don't touch anything.
<</narrative>>
<</if>>
<</if>>
<<silently>>
<<set $brotherPornPhase2Step = _step>>
<<if _step === 3 && $characters && $characters.player>>
<<set $characters.player.bbcAddict = ($characters.player.bbcAddict || 0) + 1>>
<</if>>
<<gainStat "arousal" _deepArousal>>
<<loseStat "energy" 6>>
<<advanceTime 30>>
<<if _step >= 4>>
<<set $flags.brotherPornSecondPeek = true>>
<<set $flags.brotherPornLookButtonUnlocked = true>>
<</if>>
<<if $questState && $questState.active && $questState.active.brother_browser_rabbit_hole>>
<<if _step >= 4>>
<<completeQuest "brother_browser_rabbit_hole">>
<<else>>
<<advanceQuestStage "brother_browser_rabbit_hole">>
<</if>>
<</if>>
<<flushNotifications>>
<</silently>>
<div class="location-actions">
<<btn "Go Back" _returnPassage>><</btn>>
</div>
<</nobr>>
<<nobr>>
<<silently>>
<<if $porn.source === "brotherRoom">>
<<updateCharacterLocations>>
<<set _brotherOnPC = !$_navigatingBackward && $characters.brother && $characters.brother.currentLocation === "fhBrotherRoom">>
<<if _brotherOnPC>>
<<run window.notifyWarning("Your brother walked in and made you get off the computer.")>>
<<set $location = "fhBrotherRoom">>
<<goto "fhBrotherRoom">>
<</if>>
<<set $location = "fhBrotherRoomPC">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<if $flags.brotherPornLookButtonUnlocked && $questState && $questState.active && $questState.active.brother_browser_rabbit_hole>>
<<completeQuest "brother_browser_rabbit_hole">>
<</if>>
<<set _closePassage = $porn.returnPassage || "brotherComputerMenu">>
<</silently>>
<<narrative "HotHub">>
<<if $porn.source === "brotherRoom">>
The fake logo loads in red and black, bright enough to make the room feel darker around it.
You keep the volume low and the hallway in the corner of your eye. This is still your brother's computer, and every small sound outside the room makes the site feel louder.
<<else>>
The fake logo loads in red and black against your own screen. The room is quieter here, softer around the edges, but that almost makes the choice feel more personal.
There is no browser history to blame this time. Just your door, your bed, and the site waiting in front of you.
<</if>>
Only a few categories are visible. The rest of the site seems to be hiding behind your own nerve, waiting for you to click deeper over time.
<</narrative>>
<div class="location-actions">
<<if $porn.categoriesUnlocked.includes("handjob")>>
<<btn "Handjob" "pornWatchHandjob" "default" 12>><</btn>>
<</if>>
<<if $porn.categoriesUnlocked.includes("blowjob")>>
<<btn "Blowjob" "pornWatchBlowjob" "default" 12>><</btn>>
<</if>>
<<btn "Close HotHub" _closePassage>><</btn>>
</div>
<</nobr>>
<<nobr>>
<<silently>>
<<if $porn.source === "brotherRoom">>
<<updateCharacterLocations>>
<<set _brotherOnPC = !$_navigatingBackward && $characters.brother && $characters.brother.currentLocation === "fhBrotherRoom">>
<<if _brotherOnPC>>
<<run window.notifyWarning("Your brother walked in and made you get off the computer.")>>
<<set $location = "fhBrotherRoom">>
<<goto "fhBrotherRoom">>
<</if>>
<<set $location = "fhBrotherRoomPC">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<set _cat = $porn.pendingUnlock || "">>
<<set _labels = {
handjob: "Handjob",
blowjob: "Blowjob",
vanilla: "Vanilla Sex",
anal: "Anal",
lesbian: "Lesbian",
bbc: "BBC",
creampie: "Creampie",
mff: "MFF",
mmf: "MMF",
gangbang: "Gangbang",
incest: "Incest"
}>>
<<set _label = _labels[_cat] || _cat>>
<<if _cat === "" || $porn.categoriesUnlocked.includes(_cat)>>
<<goto "pornSite_hotHub">>
<</if>>
<</silently>>
<<narrative "Something New">>
<<if $porn.source === "brotherRoom">>
The site shifts after the last video closes.
A new tag appears where there was only empty space before: <strong><<print _label>></strong>.
For a second you only stare at it. It feels less like the site changed and more like you changed enough to notice where to click next.
<<else>>
The site shifts after the last video closes.
A new tag appears where there was only empty space before: <strong><<print _label>></strong>.
In your own room, with no one else's browser history to blame, the new category feels like something you unlocked in yourself.
<</if>>
<</narrative>>
<<silently>>
<<run $porn.categoriesUnlocked.push(_cat)>>
<<if _cat === "handjob" || _cat === "blowjob" || _cat === "vanilla">>
<<set $porn.categoryTargets[_cat] = $porn.categoryTargets[_cat] || random(3, 5)>>
<<elseif _cat === "anal" || _cat === "lesbian" || _cat === "bbc" || _cat === "creampie">>
<<set $porn.categoryTargets[_cat] = $porn.categoryTargets[_cat] || random(4, 6)>>
<<else>>
<<set $porn.categoryTargets[_cat] = $porn.categoryTargets[_cat] || random(5, 8)>>
<</if>>
<<set $porn.pendingUnlock = "">>
<<flushNotifications>>
<</silently>>
<div class="location-actions">
<<btn "Open HotHub" "pornSite_hotHub" "default">><</btn>>
</div>
<</nobr>>
<<nobr>>
<<narrative "HotHub">>
<<if $porn.source === "brotherRoom">>
<<switch _introVariant>>
<<case 1>>You slip into HotHub quickly, one ear still listening for footsteps in the hallway. Your eyes scan the categories and lock onto <strong><<print _categoryLabel>></strong> before you can talk yourself out of it.
<<case 2>>The silence in your brother's room feels heavy. You turn the volume down low and click <strong><<print _categoryLabel>></strong>, the safest-looking option.
<<case 3>>Your heart is already beating fast. You don't scroll much; one thumbnail under <strong><<print _categoryLabel>></strong> catches your eye and you open it immediately.
<<case 4>>You tell yourself "just a quick look." Then your cursor moves anyway, landing on <strong><<print _categoryLabel>></strong>.
<<case 5>>The glowing screen feels too bright in the dark room. You hesitate, then tap <strong><<print _categoryLabel>></strong> before the nervousness wins.
<</switch>>
<<else>>
<<switch _introVariant>>
<<case 1>>In your own room, the screen's glow feels private. You open HotHub and your eyes keep returning to <strong><<print _categoryLabel>></strong>. You click it.
<<case 2>>Door locked, volume low. You hover over the categories, then choose <strong><<print _categoryLabel>></strong> before the silence changes your mind.
<<case 3>>Doing this in your own bed feels more real. You scroll just enough and open the first clip under <strong><<print _categoryLabel>></strong>.
<<case 4>>HotHub opens like something you chose yourself. No excuses this time. You go straight for <strong><<print _categoryLabel>></strong>.
<<case 5>>The room is quiet except for the soft fan of your laptop. You stop on <strong><<print _categoryLabel>></strong> and press play.
<</switch>>
<</if>>
<</narrative>>
<<narrative>>
<<switch _category>>
<<case "handjob">>
<<if _variant === 1>>
<<set _hjTextVariant = random(1, 3)>>
<<switch _hjTextVariant>>
<<case 1>>She straddles his lap, kissing him deeply as her hand strokes his cock with quick, eager motions. Their lips barely break apart.
<<case 2>>Pressed tight against him, she works his cock with a firm grip, her breathing growing heavier with every slick stroke.
<<case 3>>Their kiss turns messy while her hand moves faster, the wet sound of skin echoing between them.
<</switch>>
<<elseif _variant === 2>>
<<set _hjTextVariant = random(1, 3)>>
<<switch _hjTextVariant>>
<<case 1>>Kneeling between his legs on the bed, she strokes him slowly with long, deliberate pulls, her eyes flicking up to his face.
<<case 2>>Leaning over his body, she wraps her fingers tightly around his shaft and pumps with calm, confident rhythm.
<<case 3>>She squeezes and slides her hand from base to tip, watching every twitch and breath he makes.
<</switch>>
<<elseif _variant === 3>>
<<set _hjTextVariant = random(1, 3)>>
<<switch _hjTextVariant>>
<<case 1>>Her wet hand grips him tightly and strokes faster. The head of his cock begins to pulse.
<<case 2>>Thick ropes of cum spill over her fingers as she keeps pumping through his release.
<<case 3>>Her strokes turn short and urgent until he explodes across her palm and wrist in heavy spurts.
<</switch>>
<<elseif _variant === 4>>
<<set _hjTextVariant = random(1, 4)>>
<<switch _hjTextVariant>>
<<case 1>>Leaning forward, she strokes him with long, slow pulls while her heavy breasts sway gently with each motion.
<<case 2>>She keeps a tight grip, pumping steadily as her full breasts rise and fall in time with her hand.
<<case 3>>Bent over him, her palm works his cock in smooth strokes, her hanging chest rolling softly.
<<case 4>>Each deliberate stroke makes her breasts bounce heavily, her rhythm slow and controlled.
<</switch>>
<<elseif _variant === 5>>
<<set _hjTextVariant = random(1, 3)>>
<<switch _hjTextVariant>>
<<case 1>>In her maid outfit, she kneels with her breasts exposed and strokes him with steady, deliberate motions, glancing up at him.
<<case 2>>She starts slow but firm, her smile growing more confident as her rhythm smooths out.
<<case 3>>Her chest bounces freely while she pumps him faster, lips parted and eyes bright with hunger.
<</switch>>
<<elseif _variant === 6>>
<<set _hjTextVariant = random(1, 4)>>
<<switch _hjTextVariant>>
<<case 1>>A long-haired woman strokes him slowly while looking straight at him, murmuring softly, "Oh my gosh, you're so hard."
<<case 2>>She uses both hands, wet and slick, building speed as she smiles and says, "I'm loving how hard you are for me like this."
<<case 3>>Her thumb circles the tip while she watches his face, then drags him between her breasts before continuing with her hand.
<<case 4>>She strokes faster, eyes locked on him, licking her lips as her wet hand glistens under the light.
<</switch>>
<<elseif _variant === 7>>
<<set _hjTextVariant = random(1, 3)>>
<<switch _hjTextVariant>>
<<case 1>>A dark-haired woman works him with both wet hands, staring straight at the camera.
<<case 2>>She pumps faster and grins, whispering, "Mmm, it's so good... cum for me."
<<case 3>>Thick cum spills over her hands and stomach as she coos, "You're such a good boy..."
<</switch>>
<<elseif _variant === 8>>
<<set _hjTextVariant = random(1, 3)>>
<<switch _hjTextVariant>>
<<case 1>>A long brown-haired woman strokes him close to her face, lips parted, letting out soft "Ah..." sounds.
<<case 2>>She brings him near her lips and pumps faster, her expression turning more desperate with each stroke.
<<case 3>>Her hand keeps a smooth rhythm as she whispers "Ah... yes," her face flushing with hunger.
<</switch>>
<<elseif _variant === 9>>
<<set _hjTextVariant = random(1, 3)>>
<<switch _hjTextVariant>>
<<case 1>>She pulls up her black USC shirt, exposing her chest, and strokes him with slow, tight motions. Her red nails flash with every pass.
<<case 2>>Leaning forward, she pumps him steadily while her breasts sway lightly.
<<case 3>>Her gaze stays down, red nails tracing his shaft as she works him in her palm.
<</switch>>
<<elseif _variant === 10>>
<<set _hjTextVariant = random(1, 3)>>
<<switch _hjTextVariant>>
<<case 1>>A blonde kneels on the bed and grips him firmly, stroking slowly at first, her blue eyes locked on the camera.
<<case 2>>She pumps him with long, measured strokes, licking her lips as she watches his reactions.
<<case 3>>She keeps a steady rhythm, never breaking eye contact, a small pleased smile on her face.
<</switch>>
<<elseif _variant === 11>>
<<set _hjTextVariant = random(1, 4)>>
<<switch _hjTextVariant>>
<<case 1>>Leaning forward, she strokes him with long pulls while her heavy breasts sway downward.
<<case 2>>She grips him tight and pumps steadily, her breasts rolling with each motion.
<<case 3>>Her loose breasts bounce freely as her palm works him in controlled strokes.
<<case 4>>Bent over him, she keeps a firm rhythm, her chest moving heavily with every pass.
<</switch>>
<<elseif _variant === 12>>
<<set _hjTextVariant = random(1, 4)>>
<<switch _hjTextVariant>>
<<case 1>>Head tilted back, his hand tangled in her hair, she strokes his cock slowly just above her breasts.
<<case 2>>She keeps him pressed close to her lace bra, pumping steadily as her chest trembles.
<<case 3>>Her smile widens while she strokes faster. Her breasts bounce with each motion.
<<case 4>>She holds him right against the top of her breasts and strokes until he cums hard, thick white streaks landing across her chest, bra, and neck. She moans softly, head thrown back.
<</switch>>
<<else>>
She teases him slowly, smiling whenever he gets impatient. The control in her hands is what keeps your attention, and you start noticing how much power there is in making someone wait.
<</if>>
<<case "blowjob">>
<<if _variant === 1>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>She looks straight into the camera with heavy lusty eyes, sliding her wet lips all the way down his cock until it hits the back of her throat. Thick spit drips down his shaft as she holds him deep.
<<case 2>>On all fours, she strokes the base with one hand while sucking him hard, pulling back just to give a naughty smile before taking him deep again.
<<case 3>>She flips her hair back, licks slowly up the underside of his cock, then attacks it hungrily, making loud wet sucking noises.
<<case 4>>Leaning over him, she stares at the camera and bobs fast, swirling her tongue around the swollen head every time she comes up.
<<case 5>>She pulls back with a playful grin, whispers something about not messing up the sheets, then immediately wraps her glossy lips around his cock again.
<</switch>>
<<elseif _variant === 2>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The blonde stares up with big blue eyes, her soft lips stretched tight around his thick cock as she sucks him nice and slow.
<<case 2>>She twists her hand around the base while her mouth glides up and down, pulling off every few strokes to breathe and let spit drip from her lips.
<<case 3>>Her ponytail swings as she speeds up, eyes watering but still giving that hungry submissive look.
<<case 4>>She licks the whole length, opens wide, and pushes him straight down her throat with messy gagging sounds.
<<case 5>>She takes him out, tongue hanging out, long strings of spit connecting her lips to his cock while she smiles up at him.
<</switch>>
<<elseif _variant === 3>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>She gives him a lusty look before leaning in and swallowing his cock in one smooth motion.
<<case 2>>Kneeling on the bed, she teases him with long slow licks, then suddenly sucks him deep and greedy.
<<case 3>>Her hand pumps his shaft hard while her lips stay locked around the head, spit running down her fingers as she keeps eye contact.
<<case 4>>She starts bobbing faster, taking more of his cock each time, her shiny lips sliding smoothly.
<<case 5>>She pulls back, strokes him with her soaked hand while flicking her tongue on the tip, then swallows him again.
<</switch>>
<<elseif _variant === 4>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>Lying on her stomach, she looks up seductively and takes his cock between her eager lips, sucking with passion.
<<case 2>>She smiles mischievously, swirls her tongue around the head, then sinks down until her lips kiss his balls.
<<case 3>>Her hand works the shaft while she sucks harder, letting out soft moans around his cock.
<<case 4>>She laughs playfully, jerks him fast and sloppy, then dives back down with wild energy.
<<case 5>>She opens her mouth wide, sticks her tongue out and slaps his wet cock against it while staring at the camera.
<</switch>>
<<elseif _variant === 5>>
<<set _bjTextVariant = random(1, 4)>>
<<switch _bjTextVariant>>
<<case 1>>The blonde in black lingerie kneels in front of the mirror, sucking his cock while he films from above.
<<case 2>>She strokes and licks every inch, looking up submissively through the reflection.
<<case 3>>She takes him down her throat slowly, her ponytail swaying with each deep stroke.
<<case 4>>She pulls back to lick the tip, then slides him back in deep while he watches.
<</switch>>
<<elseif _variant === 6>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The long dark-haired brunette kneels and takes his cock deep with slow wet strokes.
<<case 2>>Hand around the base, she moves smoothly while keeping eye contact.
<<case 3>>Wide shot shows her in grey bra and black stockings, sucking him passionately in the luxury apartment.
<<case 4>>She gets faster and sloppier, spit dripping down his cock.
<<case 5>>She pulls back, licks and swirls her tongue around the head, then slides him deep again.
<</switch>>
<<elseif _variant === 7>>
<<set _bjTextVariant = random(1, 4)>>
<<switch _bjTextVariant>>
<<case 1>>The petite Asian in yellow lingerie lies on the bed and eagerly sucks his cock.
<<case 2>>She bobs with wet, noisy strokes, one hand working the base.
<<case 3>>She keeps looking up at him, licking and sucking like she cannot get enough.
<<case 4>>She pushes him deeper into her throat, moaning softly as she works harder.
<</switch>>
<<elseif _variant === 8>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The sexy brunette with black glasses lies on her stomach in grey lingerie, sucking his cock with wet eager lips.
<<case 2>>She strokes him firmly with her manicured hand, taking him deep while staring intensely.
<<case 3>>She bobs faster, covering his cock in spit as she works him hard.
<<case 4>>She pulls back teasingly, licks the entire shaft, then dives down again, glasses slightly fogged.
<<case 5>>She looks straight at the camera with a naughty face, tongue pressed against the tip while slowly stroking him.
<</switch>>
<<elseif _variant === 9>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The gorgeous brunette lies naked except for her pulled-down jeans, sucking the muscular blond guy's cock with real hunger.
<<case 2>>She holds the base and moves her head slowly, looking up at him as he smiles down.
<<case 3>>Eyes closed in pleasure, back arched, she takes him deeper and deeper.
<<case 4>>He leans back moaning, then props himself up to watch her suck passionately.
<<case 5>>She pulls off, gives him a seductive smile, and they lean in for a deep passionate kiss.
<</switch>>
<<elseif _variant === 10>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The brunette in the light blue top kneels in front of the standing guy, looking up with seductive eyes as she takes his cock all the way down.
<<case 2>>She grips the base tight and sucks the head, occasionally pulling back to lick the full length.
<<case 3>>One hand on his thigh, she bobs smoothly while keeping strong eye contact.
<<case 4>>She speeds up, spit dripping everywhere as she takes him deeper.
<<case 5>>She pulls back, tongue out, rubbing his cock against her lips while giving him her sluttiest look.
<</switch>>
<<elseif _variant === 11>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The stunning brunette in black lingerie lies on top of him, long hair falling over her face as she eagerly sucks his cock.
<<case 2>>With her light blue nails wrapped around the base, she bobs while staring right at the camera.
<<case 3>>Her eyes close in pleasure as she sucks wet and sloppy.
<<case 4>>She strokes him hard, licks the full length, then dives back down faster.
<<case 5>>She pulls back, tongue out against the tip, giving the camera a filthy smile while stroking.
<</switch>>
<<elseif _variant === 12>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The brunette with glasses and beige sweater kneels on the kitchen floor, taking his cock deep into her mouth.
<<case 2>>She uses both hands to stroke the base while sucking and swirling her tongue around the head.
<<case 3>>She keeps eye contact and bobs with smooth wet strokes, taking him further each time.
<<case 4>>She pushes herself harder, spit dripping as her head moves faster.
<<case 5>>She pulls back, tongue out, rubbing his cock on her glossy lips with glassy eyes.
<</switch>>
<<elseif _variant === 13>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The topless brunette with light brown hair kneels on the bathroom floor, looking up as she swallows his cock.
<<case 2>>Both hands stroking firmly, she sucks with wet noisy passion and strong eye contact.
<<case 3>>She takes him deeper, spit running down, pulling back only to tease the head.
<<case 4>>She goes faster, hands and mouth working together desperately.
<<case 5>>At the end she pulls back, smiles playfully at the camera with the tip resting on her lips.
<</switch>>
<<elseif _variant === 14>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The short-haired brunette kneels on the carpet, focused and intense as she sucks and strokes his cock.
<<case 2>>She takes him deep, eyes squinting, sunlight shining on the spit covering her chin.
<<case 3>>She speeds up, getting messy and aggressive, barely pausing to breathe.
<<case 4>>Mouth wide open, tongue out, she licks and sucks the head hungrily.
<<case 5>>She stays totally in the zone, sucking and stroking passionately till the end.
<</switch>>
<<elseif _variant === 15>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The long-haired brunette in the blue top kneels in the bathroom, sucking his cock while looking up at him.
<<case 2>>Her ringed hand strokes the base, eyes closed at first then opening to give strong eye contact.
<<case 3>>She moves smoothly and wetly, one hand roaming over his body.
<<case 4>>She pulls back to lick the entire cock, then dives in again with fresh hunger.
<<case 5>>Towards the end she looks up seductively, playing with the tip against her lips.
<</switch>>
<<elseif _variant === 16>>
<<set _bjTextVariant = random(1, 5)>>
<<switch _bjTextVariant>>
<<case 1>>The beautiful brunette with black glasses kneels in front of the muscular guy, sucking his cock with pure hunger.
<<case 2>>One hand on the shaft, she bobs while looking up through her glasses.
<<case 3>>In the wide shot she is in grey lingerie and stockings, kneeling submissively as he stands over her.
<<case 4>>She picks up speed, hair swaying, taking him deeper and deeper.
<<case 5>>She pulls back, swirls her tongue around the tip, then dives down again with sloppy passion.
<</switch>>
<<else>>
The camera catches her eyes more than anything else. The performance is crude, but the way she holds attention makes the whole scene feel less passive than you expected.
<</if>>
<<case "vanilla">>
<<if _position === "cowgirl">>
The woman moves on top, setting the rhythm herself. The position looks almost familiar, but watching her control the pace makes it feel like something you had only understood on the surface.
<<elseif _position === "doggy">>
The scene is rougher, more about rhythm and trust than romance. You watch how quickly the mood changes when neither of them slows down.
<<else>>
It is slower and face-to-face, almost intimate enough to make the screen feel too close. The softness of it catches you off guard more than the nudity.
<</if>>
<<case "anal">>
<<if _variant === 1>>
The scene takes its time. The tension is the point, and you keep watching for the moment hesitation turns into trust, then into something more intense.
<<else>>
It looks intense, careful at first, then less careful once both of them settle into it. You are not sure whether the nervousness in your stomach is discomfort or curiosity.
<</if>>
<<case "bbc">>
<<if _variant === 1>>
The video clearly wants you to notice the contrast, and it works. You catch yourself wondering how much of the reaction is fear, how much is performance, and how much is wanting.
<<else>>
The woman looks overwhelmed at first, then focused. That shift is what stays with you, the way nervousness becomes attention instead of retreat.
<</if>>
<<case "creampie">>
<<if _variant === 1>>
The whole clip seems aimed at the ending. You know that from the title, and still you wait for it, feeling the scene pull everything toward that one moment.
<<else>>
She clings to him near the end, and the intimacy of not pulling away feels almost more shocking than the act itself.
<</if>>
<<case "lesbian">>
<<if _variant === 1>>
The clip is slower and more focused on expression than shock. You end up watching how confidence shifts between them more than anything explicit.
<<else>>
The pace is gentler, but no less intimate. You keep watching the eye contact and touch, surprised by how much tension can build without aggression.
<</if>>
<<case "mff">>
<<if _variant === 1>>
The scene is crowded from the start, but it moves with clear rhythm. You expected chaos, yet the way each person takes a turn feels almost rehearsed.
<<else>>
The clip keeps layering reactions and touch, pulling your focus in two directions at once. You keep watching because the pacing stays controlled despite the intensity.
<</if>>
<<case "mmf">>
<<if _variant === 1>>
The setup is intense enough that you almost pause it, then keep going. Once the rhythm settles, what hooks you is coordination more than raw shock.
<<else>>
You keep trying to decode where your eyes should stay: faces, hands, reactions, all at once. The arrangement feels dense, but oddly structured once you adjust.
<</if>>
<<case "gangbang">>
<<if _variant === 1>>
The clip feels chaotic at first. Then patterns appear: hands, voices, bodies taking turns around one woman. It is overwhelming, but not as random as it looked.
<<else>>
It is overwhelming in a way that makes you keep looking, trying to understand why she does not disappear inside it. The attention on her feels almost heavier than the bodies.
<</if>>
<<case "incest">>
<<if _variant === 1>>
<<if $porn.source === "brotherRoom">>
The acting is fake, but the words are not easy to ignore. Watching it from your brother's room makes the category feel too close to the walls around you.
<<else>>
The acting is fake, but the words are not easy to ignore. Alone in your own room, the silence makes the category feel more dangerous than the video should.
<</if>>
<<else>>
You tell yourself it is only a tag and only acting. The thought does not make watching it feel less dangerous.
<</if>>
<</switch>>
<</narrative>>
<<if _category === "handjob">>
<<set _hjInnerVoiceVariant = random(1, 12)>>
<<dialog "player">>
(Inner voice)
<<switch _hjInnerVoiceVariant>>
<<case 1>>If I did that with my hand... would he make the same sounds?
<<case 2>>Her hand looks so sure of itself. I keep wondering if mine would shake.
<<case 3>>Just a hand... and he's completely losing control. That's actually scary.
<<case 4>>I shouldn't be imagining myself in her place... but I can't stop.
<<case 5>>How does it feel for her? The weight, the heat, the way it twitches...
<<case 6>>If I touched someone like that, would I feel powerful or completely embarrassed?
<<case 7>>She changes her rhythm and he reacts instantly. I never knew a hand could do that.
<<case 8>>My face is burning. Why am I trying to memorize how she's holding him?
<<case 9>>I keep picturing my own fingers wrapped around it... slow at first, then tighter.
<<case 10>>She seems to enjoy it. Not just doing it, but really enjoying his reactions.
<<case 11>>What would it feel like if he throbbed like that because of me?
<<case 12>>I'm not supposed to be thinking about this... but I can't look away.
<</switch>>
<</dialog>>
<<elseif _category === "blowjob">>
<<set _bjInnerVoiceVariant = random(1, 10)>>
<<dialog "player">>
(Inner voice)
<<switch _bjInnerVoiceVariant>>
<<case 1>>She's wearing almost nothing... and she's doing it so confidently. How is she not embarrassed?
<<case 2>>Her ponytail keeps swinging every time she moves her head... it's almost hypnotic.
<<case 3>>She's using both her hand and her mouth at the same time... I never thought it would look like this.
<<case 4>>Look at her eyes... she's actually looking up at him while doing it. Does that make it feel better?
<<case 5>>So much saliva... it's dripping everywhere. Is it supposed to be this wet?
<<case 6>>Her breasts are pressed together in that bodysuit... I can see her nipples through the fabric.
<<case 7>>She's taking him so deep... doesn't her throat hurt? How does she do that?
<<case 8>>The way she's kneeling there... submissive but in control at the same time. It's confusing me.
<<case 9>>If I were her... could I ever suck someone like that? With that much hunger in my eyes?
<<case 10>>God... I'm getting wet just watching this. What's wrong with me?
<</switch>>
<</dialog>>
<</if>>
<<vid $porn.repeatVidSrc $porn.repeatVidWidth>>
<<narrative>>
<<if $porn.source === "brotherRoom">>
<<switch _closingVariant>>
<<case 1>>You close the tab fast and sit motionless in his chair. The heat between your legs refuses to fade.
<<case 2>>The screen goes blank. You stay sitting there, heart still racing, trying to act like nothing happened.
<<case 3>>You exit the site and listen at the door. Everything is quiet... except the warmth still pulsing under your skin.
<<case 4>>The video ends. You clear the browser quickly, but the images stay burned behind your eyes.
<<case 5>>You push the chair back and stand up on slightly shaky legs. Doing this in his room somehow makes the guilt sharper.
<</switch>>
<<else>>
<<switch _closingVariant>>
<<case 1>>You close the tab and sit in the silence of your room. The warmth inside you lingers.
<<case 2>>The screen darkens. You stay there, breathing a little heavier than before.
<<case 3>>You shut HotHub and stare at your desktop. Your body hasn't caught up with the decision to stop.
<<case 4>>The video ends. You close everything, but the flush on your face and the heat between your thighs stay.
<<case 5>>You lean back and let out a slow breath. No one knows... but you definitely do.
<</switch>>
<</if>>
<</narrative>>
<</nobr>><<nobr>>
<<narrative "Browsing">>
You sit down at your own computer in your room.
<</narrative>>
<<narrative>>
For a while, you only stare at the blank screen. Then the thought comes on its own: porn.
<</narrative>>
<<narrative>>
You move the mouse. A short hesitation. Then the site opens.
<</narrative>>
<<narrative>>
Categories spread across the page. Some feel too blunt, some feel too heavy.
<</narrative>>
<<narrative>>
Your eyes stop on the one that looks the most harmless — <strong>Handjob</strong> — and you click.
<</narrative>>
<<vid "assets/content/scenes/global/watchporn/handjob/hjfirst1.mp4" "60%">>
<<narrative>>
The video loads on a woman on her knees, her hand wrapped around the man's cock, slow and almost curious. Your heartbeat picks up.
<</narrative>>
<<dialog "player">>
(Inner voice)
I'm really watching this...
<</dialog>>
<<narrative>>
Her hand finds a rhythm. Her thumb circles the tip while the man's breathing changes, and she notices and adjusts her pace.
<</narrative>>
<<dialog "player">>
(Inner voice)
Why am I imagining this?
<</dialog>>
<<vid "assets/content/scenes/global/watchporn/handjob/hjfirst2.mp4" "60%">>
<<narrative>>
The rhythm builds. The man's body tightens. She smiles, and the smile stays on screen a second too long. Your stomach pulls tight, because she is not just doing it — she likes it.
<</narrative>>
<<narrative>>
The video ends. More clips from the same category appear below: rows of thumbnails, rows of titles. For a moment, you look at all of them.
<</narrative>>
<<dialog "player">>
(Inner voice)
Enough for today...
<</dialog>>
<<narrative>>
You close the site.
<</narrative>>
<<narrative>>
You sit there for a while, trying to steady your breathing. Deep breaths help, but not much.
<</narrative>>
<<narrative>>
Even with your door locked, you feel strangely exposed.
<</narrative>>
<<narrative>>
This is the first time you watched porn on purpose, and it made you feel far more than you expected.
<</narrative>>
<</nobr>><<nobr>>
<<narrative "Browsing">>
You sit down at your own computer in your room.
<</narrative>>
<<narrative>>
For a while, you only stare at the blank screen. Then the thought comes on its own: porn.
<</narrative>>
<<narrative>>
You move the mouse. A short hesitation. Then the site opens.
<</narrative>>
<<narrative>>
Categories spread across the page. Some feel too blunt, some feel too heavy.
<</narrative>>
<<narrative>>
Your eyes stop on the one that looks the most harmless — <strong>Handjob</strong> — and you click.
<</narrative>>
<<vid "assets/content/scenes/global/watchporn/handjob/hjfirst1.mp4" "60%">>
<<narrative>>
The video loads on a woman on her knees, her hand wrapped around the man's cock, slow and almost curious. Your heartbeat picks up.
<</narrative>>
<<dialog "player">>
(Inner voice)
I'm really watching this...
<</dialog>>
<<narrative>>
Her hand finds a rhythm. Her thumb circles the tip while the man's breathing changes, and she notices and adjusts her pace.
<</narrative>>
<<dialog "player">>
(Inner voice)
Why am I imagining this?
<</dialog>>
<<vid "assets/content/scenes/global/watchporn/handjob/hjfirst2.mp4" "60%">>
<<narrative>>
The rhythm builds. The man's body tightens. She smiles, and the smile stays on screen a second too long. Your stomach pulls tight, because she is not just doing it — she likes it.
<</narrative>>
<<narrative>>
The video ends. More clips from the same category appear below: rows of thumbnails, rows of titles. For a moment, you look at all of them.
<</narrative>>
<<dialog "player">>
(Inner voice)
Enough for today...
<</dialog>>
<<narrative>>
You close the site.
<</narrative>>
<<narrative>>
You sit there for a while, trying to steady your breathing. Deep breaths help, but not much.
<</narrative>>
<<narrative>>
Even with your door locked, you feel strangely exposed.
<</narrative>>
<<narrative>>
This is the first time you watched porn on purpose, and it made you feel far more than you expected.
<</narrative>>
<</nobr>>
<<nobr>>
<<narrative "Browsing">>
You sit down at your brother's computer again and scroll the recommendations like you always do.
<</narrative>>
<<narrative>>
Handjob clips have started to feel familiar, so tonight you are not really looking for anything.
<</narrative>>
<<narrative>>
A thumbnail suddenly stops you: a freckled brunette on her knees, lips close to him. The title is blunt - <strong>Blowjob</strong>.
<</narrative>>
<<narrative>>
Your cheeks burn instantly. You scroll past it once... then slide back up. The mouse hovers. Your stomach tightens. Curiosity wins. You click.
<</narrative>>
<<dialog "player">>
(Inner voice)
Why am I even clicking this...
<</dialog>>
<<vid "assets/content/scenes/global/watchporn/blowjob/blowjobFirst1.mp4" "100%">>
<<narrative>>
The video opens on a beautiful freckled brunette in a white lace top. She looks up at the man with hungry eyes while slowly stroking him.
<</narrative>>
<<narrative>>
She kisses the tip first, then her tongue comes out, slow and wet, licking around the head.
<</narrative>>
<<narrative>>
She takes him into her mouth and starts sucking with smooth, wet strokes. The sounds are so much louder and messier than handjobs. Her eyes keep looking up at his face, like she wants to see what she is doing to him.
<</narrative>>
<<dialog "player">>
(Inner voice)
She actually looks like she is really enjoying this...
<</dialog>>
<<narrative>>
You do not even notice you have leaned closer to the screen. Your breathing has gotten heavier without you realizing.
<</narrative>>
<<vid "assets/content/scenes/global/watchporn/blowjob/blowjobFirst2.mp4" "100%">>
<<narrative>>
The scene continues from a closer angle. Her hand works the base while her mouth takes more of him. The man grabs her long hair, holding it tightly as she continues.
<</narrative>>
<<narrative>>
She pulls back sometimes, tongue swirling around the tip, a shiny string of saliva still connecting her lips to him, then goes back down eagerly.
<</narrative>>
<<dialog "player">>
(Inner voice)
God... she really loves sucking him.
<</dialog>>
<<narrative>>
For a moment she looks straight into the camera - eyes half-lidded, lips stretched tight. Your heart skips.
<</narrative>>
<<dialog "player">>
(Inner voice)
If that was me... what would it feel like?
<</dialog>>
<<narrative>>
The video ends with her still focused and hungry, saliva on her lips. You sit there flushed, thighs pressed together, the room feeling way too warm.
<</narrative>>
<<narrative>>
You clear the tab and listen for movement in the hallway before pulling back from your brother's desk.
<</narrative>>
<</nobr>><<nobr>>
<<narrative "Browsing">>
You sit down at your computer again and scroll the recommendations like you always do.
<</narrative>>
<<narrative>>
Handjob clips have started to feel familiar, so tonight you are not really looking for anything.
<<narrative>>
<</narrative>>
A thumbnail suddenly stops you: a freckled brunette on her knees, lips close to him. The title is blunt, <strong>Blowjob</strong>.
<</narrative>>
<<narrative>>
Your cheeks burn instantly. You scroll past it once… then slide back up. The mouse hovers. Your stomach tightens. Curiosity wins. You click.
<</narrative>>
<<dialog "player">>
(Inner voice)
Why am I even clicking this...
<</dialog>>
<<vid "assets/content/scenes/global/watchporn/blowjob/blowjobfirst1.mp4" "100%">>
<<narrative>>
The video opens on a beautiful freckled brunette in a white lace top. She looks up at the man with hungry eyes while slowly stroking him.
<<narrative>>
<</narrative>>
She kisses the tip first, then her tongue comes out, slow and wet, licking around the head.
<</narrative>>
<<narrative>>
She takes him into her mouth and starts sucking with smooth, wet strokes. The sounds are so much louder and messier than handjobs.
<<narrative>>
<</narrative>>
Her eyes keep looking up at his face, like she wants to see what she is doing to him.
<</narrative>>
<<dialog "player">>
(Inner voice)
She actually looks like she is really enjoying this…
<</dialog>>
<<narrative>>
You do not even notice you have leaned closer to the screen. Your breathing has gotten heavier without you realizing.
<</narrative>>
<<vid "assets/content/scenes/global/watchporn/blowjob/blowjobfirst2.mp4" "100%">>
<<narrative>>
The scene continues from a closer angle. Her hand works the base while her mouth takes more of him. The man’s hand grabs her hair, holding it tightly.
<</narrative>>
<<narrative>>
She pulls back sometimes, tongue swirling around the tip, a shiny string of saliva still connecting her lips to him, then goes back down eagerly.
<</narrative>>
<<dialog "player">>
(Inner voice)
God… she really loves sucking him.
<</dialog>>
<<narrative>>
For a moment she looks straight into the camera, eyes half-lidded, lips stretched tight. Your heart skips.
<</narrative>>
<<dialog "player">>
(Inner voice)
If that was me… what would it feel like?
<</dialog>>
<<narrative>>
The video ends with her still focused and hungry, saliva dripping from her lips. You sit there flushed, thighs pressed together, the room feeling way too warm.
<</narrative>>
<</nobr>><<nobr>>
<<if $_navigatingBackward>>
<<run setTimeout(function(){ window.__navigatingBackwardFromUI = true; Engine.backward(); }, 0);>>
<<else>>
<<silently>>
<<set $location = "fhBedroom">>
<<set _arousalNow = parseInt($arousal || 0)>>
<</silently>>
<<if _arousalNow lt 70>>
<<goto "sleep">>
<<else>>
<<if !($flags && $flags.firstCantSleepStoryDone)>>
<<goto "nightRestless_highArousal_intro">>
<<else>>
<<goto "sleep">>
<</if>>
<</if>>
<</if>>
<</nobr>>
<<nobr>>
<<silently>>
<<set $location = "fhBedroom">>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<set $nightRestlessEventDayKey = _dateKey>>
<<set $nightRestlessSource = "highArousal">>
<</silently>>
<<narrative "Bed">>
The room is pitch dark. You've been tossing and turning for hours, but sleep won't come.
<</narrative>>
<<narrative>>
Your heart beats faster than normal. A stubborn, throbbing heat between your legs refuses to fade, no matter how much you try to ignore it.
<</narrative>>
<<dialog "player">>
(Inner voice)
Why am I like this tonight...?
<</dialog>>
<<narrative>>
You give up and grab your phone. You search things like "can't sleep at night body restless", "is strong desire normal", and "masturbating while virgin".
<</narrative>>
<<narrative>>
Many girls describe the exact same feelings. Some even write the word openly:
<</narrative>>
<<narrative>>
<strong>Masturbation.</strong>
<</narrative>>
<<narrative>>
You open a new tab:
<</narrative>>
<<narrative>>
"how to masturbate for the first time as a girl"
<</narrative>>
<<narrative>>
"what does first orgasm feel like"
<</narrative>>
<<image "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/fiddit.webp" "100%">>
<<narrative>>
A suggested video appears: "Beginner girls - first time masturbation, slow and gentle"
<</narrative>>
<<narrative>>
You lower the volume to the minimum and press play. The woman on screen speaks calmly: "Be gentle with yourself. This isn't a race."
<</narrative>>
<<narrative>>
You place the phone beside your pillow. Your heart is racing.
<</narrative>>
<<dialog "player">>
(Inner voice)
If I do this... there's no going back.
<</dialog>>
<<dialog "player">>
(Inner voice)
"Maybe I shouldn't..."
<</dialog>>
<<narrative>>
But the pressure in your body is too strong now.
<</narrative>>
<div class="location-actions">
<<btn "Continue" "nightRestless_highArousal_part2">><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "fhBedroom">>
<</silently>>
<<narrative "Bed">>
You slide your hand under your pajamas. Your fingers brush over soft hair, then find warm, slick skin. The amount of wetness surprises you.
<</narrative>>
<<narrative>>
With your middle finger, you start drawing slow circles over your clit. The first touch sends a sharp shiver through your entire body, it's incredibly sensitive. You keep going, breathing heavier, slowly finding a rhythm.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/firstmasturbate1.mp4" "100%">>
<<dialog "player">>
(Inner voice)
I'm already this wet...
<</dialog>>
<<narrative>>
You move lower and gently tease your entrance with your fingertip. Knowing you're still a virgin, you don't push inside, you only circle the tight, dripping opening, feeling it pulse against your finger.
<</narrative>>
<<narrative>>
Returning to your clit, you use two fingers with bolder strokes. Wet sounds fill the quiet room. Your cheeks burn with embarrassment, but the pleasure keeps you going.
<</narrative>>
<<narrative>>
Your legs begin to tremble. Your hips lift on their own. The pressure in your lower belly grows tighter and hotter until -
<</narrative>>
<<narrative>>
Suddenly your whole body tenses.
<</narrative>>
<<vid "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/firstmasturbate2.mp4" "100%">>
<<narrative>>
A powerful wave crashes over you. Your clit throbs hard as intense contractions rip through your core. Short, shaky moans escape your lips while your muscles clench and release again and again.
<</narrative>>
<<narrative>>
The orgasm feels endless. Warmth floods between your legs as you tremble uncontrollably.
<</narrative>>
<<dialog "player">>
(Inner voice)
I... I just came... for the first time...
<</dialog>>
<<narrative>>
You lie there sweaty, breathless, and too sensitive to move. Your heart is still pounding.
<</narrative>>
<<silently>>
/* Same as <<bedMasturbate>> — orgasm clears arousal */
<<if !$firsts.firstMasturbation>>
<<set _mtH = String($timeSys.hour || 0).padStart(2, "0")>>
<<set _mtM = String($timeSys.minute || 0).padStart(2, "0")>>
<<set $firsts.firstMasturbation = "Day " + $timeSys.day + " (" + $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year + " " + _mtH + ":" + _mtM + ")">>
<</if>>
<<set $sexual.counts.masturbation = ($sexual.counts.masturbation || 0) + 1>>
<<loseStat "arousal" 999>>
<<recalculateStats>>
<<set $flags.firstCantSleepStoryDone = true>>
<<set $flags.masturbationUnlocked = true>>
<<set $flags.nightRestlessUnlocked = true>>
<<set $nightRestlessEventSeenCount = ($nightRestlessEventSeenCount || 0) + 1>>
<<flushNotifications>>
<<run window.showNotification({ type: 'unlock', message: 'Masturbation unlocked', duration: 4500 })>>
<</silently>>
<div class="location-actions">
<<btn "Go to Sleep" "sleep">><</btn>>
</div>
<</nobr>><<nobr>>
<<silently>>
<<set $location = "fhBedroom">>
/* Easy-to-extend list: add new entries as { path, size } */
<<set _masturbateVideos = [
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing1.mp4", size: "100%" },
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing2.mp4", size: "60%" },
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing3.mp4", size: "100%" },
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing4.mp4", size: "100%" },
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing5.mp4", size: "100%" },
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing6.mp4", size: "100%" },
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing7.mp4", size: "100%" },
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing8.mp4", size: "100%" },
{ path: "assets/content/scenes/maplewood/familyHouse/bedroom/masturbate/masturbaterubbing9.mp4", size: "100%" }
]>>
<<set _masturbatePick = random(0, _masturbateVideos.length - 1)>>
<<set _masturbateVideo = _masturbateVideos[_masturbatePick].path>>
<<set _masturbateVideoSize = _masturbateVideos[_masturbatePick].size>>
<<set $sexual.counts.masturbation = ($sexual.counts.masturbation || 0) + 1>>
<<loseStat "arousal" 999>>
<<loseStat "stress" 8>>
<<loseStat "energy" 10>>
<<advanceTime 20>>
<</silently>>
<<narrative "Bed">>
You close your eyes and let your breathing settle into a slow rhythm.
Your hand moves between your legs, following the heat until it sharpens and spills over in a trembling release.
When it fades, your muscles loosen and your thoughts finally quiet down.
<</narrative>>
<<vid _masturbateVideo _masturbateVideoSize>>
<div class="location-actions">
<<btn "Back" "fhBed">><</btn>>
</div>
<</nobr>>
<div class="divnobr">
<<silently>>
<<set $location = "dinerRubys">>
<</silently>>
<<narrative>>
You clock out and push open the back door. The cold night air hits you after hours in the hot kitchen.
<</narrative>>
<<dialog "dinerWaitress3">>
Yo, $player.firstName, wait up a second.
<</dialog>>
<<narrative>>
Jake jogs a few steps and falls in beside you, apron still in his hand.
<</narrative>>
<<dialog "dinerWaitress3">>
You made it through that shitshow alive?
<</dialog>>
<<dialog "player">>
Barely.
<</dialog>>
<<dialog "dinerWaitress3">>
Same. My feet are dead.
<</dialog>>
<<dialog "dinerWaitress3">>
Hey, listen. There's a new little coffee place that just opened around the corner. Not even a five minute walk.
<</dialog>>
<<dialog "dinerWaitress3">>
Why don't we drop by after work, grab a coffee, just chill and talk for a bit. Sound good?
<</dialog>>
<<dialog "player">>
I'm beat... but yeah, a coffee actually sounds nice.
<</dialog>>
<<dialog "dinerWaitress3">>
There you go. Come on, it won't take long.
<</dialog>>
<div class="location-actions">
<<btn "Go with Jake" "dinerWork_event_jakeCoffeeInvite_followup">><</btn>>
</div>
</div>
<div class="divnobr">
<<silently>>
<<set $location = "shopCoffeeOldtown">>
<<discover "OldTown" "Old Town">>
<<discover "ShopCoffeeOldtown" "The Daily Grind">>
<<if $characters && $characters.coffeeBaristaLeo>>
<<set $characters.coffeeBaristaLeo.known = true>>
<<if !$characters.coffeeBaristaLeo.firstMet>>
<<set $characters.coffeeBaristaLeo.firstMet = $timeSys.day + "/" + $timeSys.month + "/" + $timeSys.year>>
<</if>>
<</if>>
<</silently>>
<<narrative>>
You and Jake walk the short distance to the corner. Even from outside, you can tell the place is new. The sign is still bright and the windows are spotless.
<</narrative>>
<<narrative "The Daily Grind">>
The bell above the door jingles softly as you step inside. Warm lighting, low jazz, the rich smell of fresh espresso instead of fryer oil. It's almost too peaceful after Ruby's chaos.
<</narrative>>
<<dialog "dinerWaitress3">>
Oh man. This is exactly what I needed.
<</dialog>>
<<narrative>>
A young guy behind the counter looks up from steaming a pitcher of milk and gives you both an easy smile.
<</narrative>>
<<dialog "coffeeBaristaLeo">>
Good evening. I'm Leo. Welcome to The Daily Grind. What can I get for you guys?
<</dialog>>
<<dialog "dinerWaitress3">>
Hey Leo. I'll take an americano, please.
What about you, $player.firstName?
<</dialog>>
<<narrative>>
You glance up at the chalkboard menu for a second. Way too many options for someone this tired.
<</narrative>>
<<dialog "player">>
I think I'll go with a latte. Lactose-free, if that's okay.
<</dialog>>
<<dialog "coffeeBaristaLeo">>
Of course. One americano and one lactose-free latte. Anything to eat with that?
<</dialog>>
<<dialog "dinerWaitress3">>
Just the drinks tonight. We just got off shift, food is the last thing we want right now.
<</dialog>>
<<dialog "coffeeBaristaLeo">>
Fair enough. Long day?
<</dialog>>
<<dialog "dinerWaitress3">>
Long enough.
<</dialog>>
<<narrative>>
Leo nods and turns to the espresso machine. Jake taps his card on the reader before you can even reach for your wallet.
<</narrative>>
<<dialog "player">>
Jake, you really don't have to.
<</dialog>>
<<dialog "dinerWaitress3">>
Nope. My invite, my treat. You really carried the dish pit tonight.
<</dialog>>
<<narrative>>
The grinder kicks on, then the soft hiss of the steam wand. You and Jake step a little to the side to wait by the pickup counter. It feels strange to stand on the customer side of a counter for once.
<</narrative>>
<<dialog "coffeeBaristaLeo">>
So, are you guys local, or just passing through?
<</dialog>>
<<dialog "dinerWaitress3">>
Local. Very local. We work just down the street, actually.
<</dialog>>
<<dialog "player">>
Yeah, we're at Ruby's Diner. Right around the corner from here.
<</dialog>>
<<dialog "coffeeBaristaLeo">>
Oh, the one with the red sign? I've been meaning to try it. I see the staff walk by all the time, but I haven't had the chance yet.
<</dialog>>
<<dialog "dinerWaitress3">>
You should. The food is solid. Just don't come during the dinner rush unless you enjoy chaos.
<</dialog>>
<<dialog "coffeeBaristaLeo">>
Noted. Off-hours it is.
<</dialog>>
<<narrative>>
Leo pours the steamed milk in a slow, careful spiral, and a soft leaf shape forms on top of your latte.
<</narrative>>
<<dialog "coffeeBaristaLeo">>
One americano. One lactose-free latte. Enjoy, guys.
<</dialog>>
<<dialog "dinerWaitress3">>
Thanks, Leo.
<</dialog>>
<<dialog "player">>
Thank you. It looks great.
<</dialog>>
<<narrative>>
You both grab your cups and head over to a small table by the window. The seats are softer than anything at Ruby's, and just sinking into one feels like a small reward.
<</narrative>>
<div class="location-actions">
<<btn "Continue" "dinerWork_event_jakeCoffeeInvite_gossip">><</btn>>
</div>
</div>
<div class="divnobr">
<<silently>>
<<set $location = "shopCoffeeOldtown">>
<<advanceTime 60>>
<</silently>>
<<narrative "Window Table">>
You both drop into the seats by the window. The latte is warm in your hands and the chair is the first soft thing you've sat on in hours. For a while the conversation stays easy. The dinner rush. The new POS system that nobody can figure out. How Tom somehow always ends up with the leftover fries.
<</narrative>>
<<dialog "dinerWaitress3">>
God, this is nice. Just sitting. Nobody yelling for ketchup.
<</dialog>>
<<dialog "player">>
Yeah. I didn't realize how much I needed this until I sat down.
<</dialog>>
<<dialog "dinerWaitress3">>
Right? Some shifts you just need a coffee and a chair that doesn't wobble.
<</dialog>>
<<narrative>>
You both laugh a little. Jake takes a long sip, and his shoulders finally drop.
<</narrative>>
<<dialog "dinerWaitress3">>
You're settling in pretty good, by the way. The dish pit eats most newbies alive in the first week.
<</dialog>>
<<dialog "player">>
I wouldn't call it settling in. More like surviving.
<</dialog>>
<<dialog "dinerWaitress3">>
Same thing at Ruby's.
<</dialog>>
<<narrative>>
He glances out the window for a second. The conversation slows down on its own.
<</narrative>>
<<dialog "dinerWaitress3">>
Look... Ruby's looks like normal chaos from the outside. Loud, messy, whatever. But once you've been around a while, you start seeing the patterns under all the noise.
<</dialog>>
<<dialog "player">>
What kind of patterns?
<</dialog>>
<<dialog "dinerWaitress3">>
Vince, mostly.
<</dialog>>
<<dialog "dinerWaitress3">>
He's not just a stressed boss. He's the kind of guy who makes everything personal.
<</dialog>>
<<dialog "dinerWaitress3">>
One day he's cracking jokes with everyone, next day he's riding someone hard for no reason. And he picks favorites. Always.
<</dialog>>
<<dialog "player">>
I've already seen a little of that, honestly.
<</dialog>>
<<dialog "dinerWaitress3">>
Yeah, well. It gets even weirder when Diana shows up.
<</dialog>>
<<narrative>>
His voice gets quieter, more careful.
<</narrative>>
<<dialog "dinerWaitress3">>
The second she walks through that door, Vince turns into a different person. Charming, twitchy, trying way too hard.
<</dialog>>
<<dialog "dinerWaitress3">>
Everyone in the kitchen sees it. Nobody says a word.
<</dialog>>
<<dialog "player">>
And the rest of the floor? Sofia, Emma?
<</dialog>>
<<dialog "dinerWaitress3">>
That's where it gets ugly.
<</dialog>>
<<dialog "dinerWaitress3">>
Sofia... something happened between them. Most of us know it, even if nobody says it out loud.
<</dialog>>
<<dialog "dinerWaitress3">>
He got what he wanted, cooled off after, but he still keeps her on a short leash. Better shifts. Long "talks" in the back. Little favors here and there.
<</dialog>>
<<dialog "player">>
And she's okay with that?
<</dialog>>
<<dialog "dinerWaitress3">>
She acts like she is. But the second Vince smiles at someone else, you can read it on her face. She gets jealous fast. Real fast.
<</dialog>>
<<dialog "dinerWaitress3">>
Emma's a whole different story. He tried hard with her too, I know that much. But she's not someone you can read easy.
<</dialog>>
<<dialog "dinerWaitress3">>
She doesn't talk much, doesn't react when he pushes. She just keeps her distance now. Whatever went down between them, it's still under her skin. You can feel it.
<</dialog>>
<<dialog "player">>
That's a lot heavier than I thought. I knew Vince was a piece of work, but I didn't think it ran that deep with the girls.
<</dialog>>
<<dialog "dinerWaitress3">>
Yeah. That's Ruby's behind the menu.
<</dialog>>
<<dialog "player">>
And nobody's said anything? Like, to Ruby, or higher up?
<</dialog>>
<<dialog "dinerWaitress3">>
Who's gonna say it? Sofia won't, Emma's not the type to make noise, and the rest of us need the paycheck. Vince knows that. He counts on it.
<</dialog>>
<<narrative>>
Jake leans forward, elbows on the table.
<</narrative>>
<<dialog "dinerWaitress3">>
I'm telling you all this because I actually like working with you. You show up, you do the work, you don't whine.
<</dialog>>
<<dialog "dinerWaitress3">>
You don't deserve to get dragged into any of that mess.
<</dialog>>
<<dialog "dinerWaitress3">>
So listen to me. If Vince starts paying you too much attention, getting touchy, finding little reasons to keep you late... don't sit on it.
<</dialog>>
<<dialog "dinerWaitress3">>
Tell me. Tell Emma. Don't try to handle him on your own.
<</dialog>>
<<dialog "player">>
I appreciate you saying it, Jake. Really. Most people would've just let me figure it out on my own.
<</dialog>>
<<dialog "player">>
I'll keep my distance from him, and if anything starts feeling off, you'll be the first to know.
<</dialog>>
<<dialog "dinerWaitress3">>
That's all I'm asking.
<</dialog>>
<<dialog "dinerWaitress3">>
Just keep your line clear from day one. The second you give him an inch, he takes the rest.
<</dialog>>
<<silently>>
<<gainCharacterStat "dinerWaitress3" "friendship" 4>>
<<gainStat "mood" 7>>
<<loseStat "stress" 6>>
<<recalculateStats>>
<<flushNotifications>>
<</silently>>
<<narrative>>
You both sit with that for a moment. Jake finishes his americano and turns the empty cup in his hand. Outside, a bus hisses to a stop and pulls away again. The conversation lands heavier than when you walked in, but somehow you don't mind. Feels like something honest just happened between you two.
<</narrative>>
<<dialog "dinerWaitress3">>
Alright. I should head home before I fall asleep at this table.
<</dialog>>
<<dialog "player">>
Yeah, same. Thanks again for the coffee, Jake. I needed this more than I thought.
<</dialog>>
<<dialog "dinerWaitress3">>
Anytime. Seriously.
<</dialog>>
<<dialog "dinerWaitress3">>
Get home safe, alright? See you tomorrow.
<</dialog>>
<<dialog "player">>
You too.
<</dialog>>
<<narrative>>
Jake gives you a small wave and heads off down the street. You stand there for a second, watching him go, then turn the other way.
<</narrative>>
<div class="location-actions">
<<btn "Finish your drink and leave" "shopCoffeeOldtown">><</btn>>
</div>
</div>
<<nobr>>
<<set $location = "shopCoffeeOldtown">>
<<set _shopItems = [
"coffee_takeaway_americano",
"coffee_takeaway_latte",
"coffee_takeaway_cappuccino",
"coffee_takeaway_mocha",
"coffee_takeaway_coldbrew"
]>>
<<set _shopBack = "shopCoffeeOldtown">>
<</nobr>>
<<shop "The Daily Grind" "Take Away Coffee" _shopItems _shopBack>>
<div class="divnobr">
<<nobr>>
<<set $location = $restaurantReturnPassage || "shopCoffeeOldtown">>
<<set _back = $location>>
<<set _drinkId = $dailyGrindLastDrinkId || $restaurantLastDrinkId || "dg_americano">>
<<set _drinkName = "your coffee">>
<<set _sceneText = "You sink into a chair near the window and take a slow sip while the cafe hums around you.">>
<<set _variant = random(1, 3)>>
<<advanceTime random(20, 35) "relax">>
<<switch _drinkId>>
<<case "dg_americano">>
<<set _drinkName = "Americano">>
<<switch _variant>>
<<case 1>>
<<set _sceneText = "The Americano hits quick: bitter, hot, and exactly what you needed. A few sips in, your head feels clear and your to-do list feels manageable again.">>
<<case 2>>
<<set _sceneText = "You nurse the Americano slowly, letting the strong taste settle in. By the last sip, that sleepy fog is gone and your focus is back.">>
<<default>>
<<set _sceneText = "You take your Americano black and unfiltered, no distractions. It is sharp, a little harsh, and somehow perfect for getting your mind in gear.">>
<</switch>>
<<case "dg_latte">>
<<set _drinkName = "Latte">>
<<switch _variant>>
<<case 1>>
<<set _sceneText = "The latte is smooth and warm, easy to drink without thinking. You end up sitting longer than planned, shoulders finally relaxing a little.">>
<<case 2>>
<<set _sceneText = "You wrap both hands around the latte cup and breathe out. The soft foam and mellow coffee calm your nerves more than you expected.">>
<<default>>
<<set _sceneText = "It is one of those good lattes: not too strong, not too sweet. You take your time with it, and the stress from earlier starts to fade.">>
<</switch>>
<<case "dg_cappuccino">>
<<set _drinkName = "Cappuccino">>
<<switch _variant>>
<<case 1>>
<<set _sceneText = "The cappuccino foam is thick and the espresso underneath is just right. You get a clean energy lift without feeling jittery.">>
<<case 2>>
<<set _sceneText = "You take a sip, get a bit of foam on your lip, and quietly laugh at yourself. The cappuccino gives you that balanced boost that keeps your mood steady.">>
<<default>>
<<set _sceneText = "Strong coffee, soft foam, warm cup; the cappuccino lands perfectly. By the time you finish, you feel lighter and more awake.">>
<</switch>>
<<case "dg_mocha">>
<<set _drinkName = "Mocha">>
<<switch _variant>>
<<case 1>>
<<set _sceneText = "The mocha tastes like a small reward: chocolate first, coffee right after. You catch yourself smiling between sips.">>
<<case 2>>
<<set _sceneText = "You were craving something comforting, and the mocha delivers. It is sweet enough to lift your mood without knocking you out.">>
<<default>>
<<set _sceneText = "Warm chocolate and espresso settle you in instantly. The day does not feel as heavy after finishing your mocha.">>
<</switch>>
<<case "dg_coldbrew">>
<<set _drinkName = "Cold Brew">>
<<switch _variant>>
<<case 1>>
<<set _sceneText = "The cold brew is crisp and strong, exactly the jolt you wanted. Halfway through the cup, your energy is back online.">>
<<case 2>>
<<set _sceneText = "You take a long pull from the cold brew and feel the caffeine wake-up almost immediately. It is bold, smooth, and very effective.">>
<<default>>
<<set _sceneText = "Cold, dark, and strong, this brew does not waste time. You finish it feeling sharper and ready to move again.">>
<</switch>>
<</switch>>
<<narrative "The Daily Grind">>
You enjoy your <<= _drinkName>> at The Daily Grind.
<<= _sceneText>>
<</narrative>>
<div class="location-actions">
<<btn "Back to The Daily Grind" _back>><</btn>>
</div>
<</nobr>>
</div>
<<nobr>>
<<silently>>
<<if $porn.source === "brotherRoom">>
<<updateCharacterLocations>>
<<set _brotherOnPC = !$_navigatingBackward && $characters.brother && $characters.brother.currentLocation === "fhBrotherRoom">>
<<if _brotherOnPC>>
<<run window.notifyWarning("Your brother walked in and made you get off the computer.")>>
<<set $location = "fhBrotherRoom">>
<<goto "fhBrotherRoom">>
<</if>>
<</if>>
<</silently>>
<<requireMinEnergy 12 "pornSite_hotHub">>
<<silently>>
<<set _category = "handjob">>
<<set $porn.selectedCategory = "handjob">>
<<if $porn.source === "brotherRoom">>
<<set $location = "fhBrotherRoomPC">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<if ($porn.skillGainDayKey || 0) !== _dateKey>>
<<set $porn.skillGainDayKey = _dateKey>>
<<set $porn.skillsGainedToday = []>>
<</if>>
<<if ($porn.arousalDayKey || 0) !== _dateKey>>
<<set $porn.arousalDayKey = _dateKey>>
<<set $porn.watchCountToday = 0>>
<</if>>
<<if ($porn.categoryProgressDayKey || 0) !== _dateKey>>
<<set $porn.categoryProgressDayKey = _dateKey>>
<<set $porn.categoriesProgressedToday = []>>
<</if>>
<<if ($porn.brotherRoomCategoryDailyDayKey || 0) !== _dateKey>>
<<set $porn.brotherRoomCategoryDailyDayKey = _dateKey>>
<<set $porn.brotherRoomCategoriesCountedToday = []>>
<</if>>
<</silently>>
<<if ($porn.watchCountToday || 0) >= ($porn.maxDailyWatches || 6)>>
<<narrative "HotHub">>
The site is still there, waiting exactly where you left it.
But your eyes feel tired, your body feels too warm, and every thumbnail starts to blur into the next. You have already watched enough for one day.
<</narrative>>
<div class="location-actions">
<<btn "Back to HotHub" "pornSite_hotHub">><</btn>>
</div>
<<else>>
<<silently>>
<<set $flags = $flags || {}>>
<<set $flags._actionTurnGuards = $flags._actionTurnGuards || {}>>
<<set _guardKey = "pornWatchHandjob">>
<<set _alreadyProcessedThisTurn = ($flags._actionTurnGuards[_guardKey] || 0) === State.turns>>
<<set _categoryLabel = "Handjob">>
<<if !_alreadyProcessedThisTurn>>
<<set $flags._actionTurnGuards[_guardKey] = State.turns>>
<<set _showFirstHandjob = !$porn.handjobFirstScenePlayed>>
<<set $flags.pornWatchFirstDone = true>>
<<set $flags.brotherPornWatchFirstDone = true>>
<<set $porn.categoryFirstWatchSeen.handjob = true>>
<<set $porn.categoryWatchCounts.handjob = ($porn.categoryWatchCounts.handjob || 0) + 1>>
<<if $porn.source === "brotherRoom">>
<<set $porn.brotherRoomCategoryWatchCounts.handjob = ($porn.brotherRoomCategoryWatchCounts.handjob || 0) + 1>>
<<if !$porn.brotherRoomCategoriesCountedToday.includes("handjob")>>
<<set $porn.brotherRoomCategoryDailyWatchCounts.handjob = ($porn.brotherRoomCategoryDailyWatchCounts.handjob || 0) + 1>>
<<run $porn.brotherRoomCategoriesCountedToday.push("handjob")>>
<</if>>
<</if>>
<<if !$porn.categoriesProgressedToday.includes("handjob")>>
<<set $porn.categoryProgressCounts.handjob = ($porn.categoryProgressCounts.handjob || 0) + 1>>
<<run $porn.categoriesProgressedToday.push("handjob")>>
<</if>>
<<set _count = $porn.categoryProgressCounts.handjob || 0>>
<<set _baseArousal = 10>>
<<set _arousalGain = ($porn.watchCountToday || 0) > 0 ? Math.ceil(_baseArousal / 2) : _baseArousal>>
<<set $porn.watchCountToday = ($porn.watchCountToday || 0) + 1>>
<<set _introVariant = random(1, 5)>>
<<set _variant = random(1, 12)>>
<<set _closingVariant = random(1, 5)>>
<<set _position = "">>
<<set _skills = ["handjob", "foreplay", "teasing"]>>
<<for _skill range _skills>>
<<if ($sexual.skills[_skill] || 0) < 15 && !$porn.skillsGainedToday.includes(_skill)>>
<<gainSexualSkill _skill 1>>
<<run $porn.skillsGainedToday.push(_skill)>>
<</if>>
<</for>>
<<set _videoPath = "assets/content/scenes/global/watchporn/handjob/handjob" + _variant + ".mp4">>
<<set _handjobVideoSizesList = ["50%", "100%", "100%", "100%", "50%", "50%", "50%", "50%", "50%", "50%", "100%", "50%"]>>
<<set _videoSize = _handjobVideoSizesList[_variant - 1] || "100%">>
<<set $porn.repeatVidSrc = _videoPath>>
<<set $porn.repeatVidWidth = _videoSize>>
<<gainStat "arousal" _arousalGain>>
<<gainStat "stress" 1>>
<<loseStat "energy" 7>>
<<advanceTime 10>>
<<if !$porn.categoryTargets.handjob>>
<<set $porn.categoryTargets.handjob = random(3, 5)>>
<</if>>
<<set _target = $porn.categoryTargets.handjob>>
<<if !$porn.categoriesUnlocked.includes("blowjob") && _count >= _target && ($corruption || 0) >= 1>>
<<set $porn.pendingUnlock = "blowjob">>
<</if>>
<<if _count >= 4>>
<<set $flags.privateContentHabit = true>>
<</if>>
<<flushNotifications>>
<</if>>
<</silently>>
<<if !_alreadyProcessedThisTurn>>
<<if _showFirstHandjob>>
<<if $porn.source === "brotherRoom">>
<<include "pornWatchRender_firstHandjobBrotherRoom">>
<<else>>
<<include "pornWatchRender_firstHandjobBedroom">>
<</if>>
<<set $porn.handjobFirstScenePlayed = true>>
<<else>>
<<include "pornWatchRender_repeatable">>
<</if>>
<</if>>
<div class="location-actions">
<<if $porn.pendingUnlock && $porn.pendingUnlock !== "">>
<<btn "Keep Looking" "pornUnlockEvent">><</btn>>
<<else>>
<<btn "Back to HotHub" "pornSite_hotHub">><</btn>>
<</if>>
</div>
<</if>>
<</nobr>>
<<nobr>>
<<silently>>
<<if $porn.source === "brotherRoom">>
<<updateCharacterLocations>>
<<set _brotherOnPC = !$_navigatingBackward && $characters.brother && $characters.brother.currentLocation === "fhBrotherRoom">>
<<if _brotherOnPC>>
<<run window.notifyWarning("Your brother walked in and made you get off the computer.")>>
<<set $location = "fhBrotherRoom">>
<<goto "fhBrotherRoom">>
<</if>>
<</if>>
<</silently>>
<<requireMinEnergy 12 "pornSite_hotHub">>
<<silently>>
<<set _category = "blowjob">>
<<set $porn.selectedCategory = "blowjob">>
<<if $porn.source === "brotherRoom">>
<<set $location = "fhBrotherRoomPC">>
<<else>>
<<set $location = "fhBedroom">>
<</if>>
<<if !$porn.categoriesUnlocked.includes("blowjob")>>
<<goto "pornSite_hotHub">>
<</if>>
<<set _dateKey = ($timeSys.year * 10000) + ($timeSys.month * 100) + $timeSys.day>>
<<if ($porn.skillGainDayKey || 0) !== _dateKey>>
<<set $porn.skillGainDayKey = _dateKey>>
<<set $porn.skillsGainedToday = []>>
<</if>>
<<if ($porn.arousalDayKey || 0) !== _dateKey>>
<<set $porn.arousalDayKey = _dateKey>>
<<set $porn.watchCountToday = 0>>
<</if>>
<<if ($porn.categoryProgressDayKey || 0) !== _dateKey>>
<<set $porn.categoryProgressDayKey = _dateKey>>
<<set $porn.categoriesProgressedToday = []>>
<</if>>
<<if ($porn.brotherRoomCategoryDailyDayKey || 0) !== _dateKey>>
<<set $porn.brotherRoomCategoryDailyDayKey = _dateKey>>
<<set $porn.brotherRoomCategoriesCountedToday = []>>
<</if>>
<</silently>>
<<if ($porn.watchCountToday || 0) >= ($porn.maxDailyWatches || 6)>>
<<narrative "HotHub">>
The site is still there, waiting exactly where you left it.
But your eyes feel tired, your body feels too warm, and every thumbnail starts to blur into the next. You have already watched enough for one day.
<</narrative>>
<div class="location-actions">
<<btn "Back to HotHub" "pornSite_hotHub">><</btn>>
</div>
<<else>>
<<silently>>
<<set $flags = $flags || {}>>
<<set $flags._actionTurnGuards = $flags._actionTurnGuards || {}>>
<<set _guardKey = "pornWatchBlowjob">>
<<set _alreadyProcessedThisTurn = ($flags._actionTurnGuards[_guardKey] || 0) === State.turns>>
<<set _categoryLabel = "Blowjob">>
<<if !_alreadyProcessedThisTurn>>
<<set $flags._actionTurnGuards[_guardKey] = State.turns>>
<<set _showFirstBlowjob = !$porn.blowjobFirstScenePlayed>>
<<set $flags.pornWatchFirstDone = true>>
<<set $flags.brotherPornWatchFirstDone = true>>
<<set $porn.categoryFirstWatchSeen.blowjob = true>>
<<set $porn.categoryWatchCounts.blowjob = ($porn.categoryWatchCounts.blowjob || 0) + 1>>
<<if $porn.source === "brotherRoom">>
<<set $porn.brotherRoomCategoryWatchCounts.blowjob = ($porn.brotherRoomCategoryWatchCounts.blowjob || 0) + 1>>
<<if !$porn.brotherRoomCategoriesCountedToday.includes("blowjob")>>
<<set $porn.brotherRoomCategoryDailyWatchCounts.blowjob = ($porn.brotherRoomCategoryDailyWatchCounts.blowjob || 0) + 1>>
<<run $porn.brotherRoomCategoriesCountedToday.push("blowjob")>>
<</if>>
<</if>>
<<if !$porn.categoriesProgressedToday.includes("blowjob")>>
<<set $porn.categoryProgressCounts.blowjob = ($porn.categoryProgressCounts.blowjob || 0) + 1>>
<<run $porn.categoriesProgressedToday.push("blowjob")>>
<</if>>
<<set _count = $porn.categoryProgressCounts.blowjob || 0>>
<<set _baseArousal = 12>>
<<set _arousalGain = ($porn.watchCountToday || 0) > 0 ? Math.ceil(_baseArousal / 2) : _baseArousal>>
<<set $porn.watchCountToday = ($porn.watchCountToday || 0) + 1>>
<<set _introVariant = random(1, 5)>>
<<set _variant = random(1, 16)>>
<<set _closingVariant = random(1, 5)>>
<<set _position = "">>
<<set _skills = ["oral", "foreplay", "teasing"]>>
<<for _skill range _skills>>
<<if ($sexual.skills[_skill] || 0) < 15 && !$porn.skillsGainedToday.includes(_skill)>>
<<gainSexualSkill _skill 1>>
<<run $porn.skillsGainedToday.push(_skill)>>
<</if>>
<</for>>
<<set _basePath = "assets/content/scenes/global/watchporn/">>
<<set _videoPath = _basePath + "blowjob/blowjob" + _variant + ".mp4">>
<<set _videoSize = _variant === 5 ? "60%" : "100%">>
<<set $porn.repeatVidSrc = _videoPath>>
<<set $porn.repeatVidWidth = _videoSize>>
<<gainStat "arousal" _arousalGain>>
<<gainStat "stress" 1>>
<<loseStat "energy" 7>>
<<advanceTime 10>>
<<if !$porn.categoryTargets.blowjob>>
<<set $porn.categoryTargets.blowjob = random(3, 5)>>
<</if>>
<<if _count >= 4>>
<<set $flags.privateContentHabit = true>>
<</if>>
<<flushNotifications>>
<</if>>
<</silently>>
<<if !_alreadyProcessedThisTurn>>
<<if _showFirstBlowjob>>
<<if $porn.source === "brotherRoom">>
<<include "pornWatchRender_firstBlowjobBrotherRoom">>
<<else>>
<<include "pornWatchRender_firstBlowjobBedroom">>
<</if>>
<<set $porn.blowjobFirstScenePlayed = true>>
<<else>>
<<include "pornWatchRender_repeatable">>
<</if>>
<</if>>
<div class="location-actions">
<<btn "Back to HotHub" "pornSite_hotHub">><</btn>>
</div>
<</if>>
<</nobr>>
<<nobr>>
<<silently>>
<<set $location = "fhBedroom">>
<<set $porn.source = "bedroom">>
<<set $porn.returnPassage = "laptopComputerMenu">>
<</silently>>
<<narrative "Laptop">>
You open your laptop on your bed and let the screen glow fill the room.
What do you want to do?
<</narrative>>
<div class="location-actions">
<<if $flags.brotherPornLookButtonUnlocked && ($corruption || 0) >= 1>>
<<btn "Watch Porn" "pornSite_hotHub" "default">><</btn>>
<</if>>
<<btn "Browse / Surf" "brotherComputerBrowse" "default" 15>><</btn>>
<<btn "Watch Video" "brotherComputerWatchVideo" "default" 12>><</btn>>
<<btn "Play Game" "brotherComputerPlayGame" "default" 15>><</btn>>
<<btn "Close Laptop" "fhBedroom">><</btn>>
</div>
<</nobr>>
<<nobr>>
<<set $location = "sunsetWC">>
<<set $returnAfterToilet = "sunsetWC">>
<<updateCharacterLocations>>
<<narrative "Restroom Cabin">>
You slip into a private cabin with just enough space to quickly change your outfit.
<</narrative>>
<</nobr>>
<<wardrobe "sunsetWC" "sunsetWC">>